[关闭]
@wudawufanfan 2016-12-27T07:20:11.000000Z 字数 6324 阅读 467

在此处输入标题

未分类


在此输入正文

  1. import numpy as np
  2. from matplotlib import pyplot as pl
  3. from matplotlib import animation
  4. from scipy.fftpack import fft,ifft
  5. class Schrodinger(object):
  6. """
  7. Class which implements a numerical solution of the time-dependent
  8. Schrodinger equation for an arbitrary potential
  9. """
  10. def __init__(self, x, psi_x0, V_x,
  11. k0 = None, hbar=1, m=1, t0=0.0):
  12. """
  13. Parameters
  14. ----------
  15. x : array_like, float
  16. length-N array of evenly spaced spatial coordinates
  17. psi_x0 : array_like, complex
  18. length-N array of the initial wave function at time t0
  19. V_x : array_like, float
  20. length-N array giving the potential at each x
  21. k0 : float
  22. the minimum value of k. Note that, because of the workings of the
  23. fast fourier transform, the momentum wave-number will be defined
  24. in the range
  25. k0 < k < 2*pi / dx
  26. where dx = x[1]-x[0]. If you expect nonzero momentum outside this
  27. range, you must modify the inputs accordingly. If not specified,
  28. k0 will be calculated such that the range is [-k0,k0]
  29. hbar : float
  30. value of planck's constant (default = 1)
  31. m : float
  32. particle mass (default = 1)
  33. t0 : float
  34. initial tile (default = 0)
  35. """
  36. # Validation of array inputs
  37. self.x, psi_x0, self.V_x = map(np.asarray, (x, psi_x0, V_x))
  38. N = self.x.size
  39. assert self.x.shape == (N,)
  40. assert psi_x0.shape == (N,)
  41. assert self.V_x.shape == (N,)
  42. # Set internal parameters
  43. self.hbar = hbar
  44. self.m = m
  45. self.t = t0
  46. self.dt_ = None
  47. self.N = len(x)
  48. self.dx = self.x[1] - self.x[0]
  49. self.dk = 2 * np.pi / (self.N * self.dx)
  50. # set momentum scale
  51. if k0 == None:
  52. self.k0 = -0.5 * self.N * self.dk
  53. else:
  54. self.k0 = k0
  55. self.k = self.k0 + self.dk * np.arange(self.N)
  56. self.psi_x = psi_x0
  57. self.compute_k_from_x()
  58. # variables which hold steps in evolution of the
  59. self.x_evolve_half = None
  60. self.x_evolve = None
  61. self.k_evolve = None
  62. # attributes used for dynamic plotting
  63. self.psi_x_line = None
  64. self.psi_k_line = None
  65. self.V_x_line = None
  66. def _set_psi_x(self, psi_x):
  67. self.psi_mod_x = (psi_x * np.exp(-1j * self.k[0] * self.x)
  68. * self.dx / np.sqrt(2 * np.pi))
  69. def _get_psi_x(self):
  70. return (self.psi_mod_x * np.exp(1j * self.k[0] * self.x)
  71. * np.sqrt(2 * np.pi) / self.dx)
  72. def _set_psi_k(self, psi_k):
  73. self.psi_mod_k = psi_k * np.exp(1j * self.x[0]
  74. * self.dk * np.arange(self.N))
  75. def _get_psi_k(self):
  76. return self.psi_mod_k * np.exp(-1j * self.x[0] *
  77. self.dk * np.arange(self.N))
  78. def _get_dt(self):
  79. return self.dt_
  80. def _set_dt(self, dt):
  81. if dt != self.dt_:
  82. self.dt_ = dt
  83. self.x_evolve_half = np.exp(-0.5 * 1j * self.V_x
  84. / self.hbar * dt )
  85. self.x_evolve = self.x_evolve_half * self.x_evolve_half
  86. self.k_evolve = np.exp(-0.5 * 1j * self.hbar /
  87. self.m * (self.k * self.k) * dt)
  88. psi_x = property(_get_psi_x, _set_psi_x)
  89. psi_k = property(_get_psi_k, _set_psi_k)
  90. dt = property(_get_dt, _set_dt)
  91. def compute_k_from_x(self):
  92. self.psi_mod_k = fft(self.psi_mod_x)
  93. def compute_x_from_k(self):
  94. self.psi_mod_x = ifft(self.psi_mod_k)
  95. def time_step(self, dt, Nsteps = 1):
  96. """
  97. Perform a series of time-steps via the time-dependent
  98. Schrodinger Equation.
  99. Parameters
  100. ----------
  101. dt : float
  102. the small time interval over which to integrate
  103. Nsteps : float, optional
  104. the number of intervals to compute. The total change
  105. in time at the end of this method will be dt * Nsteps.
  106. default is N = 1
  107. """
  108. self.dt = dt
  109. if Nsteps > 0:
  110. self.psi_mod_x *= self.x_evolve_half
  111. for i in xrange(Nsteps - 1):
  112. self.compute_k_from_x()
  113. self.psi_mod_k *= self.k_evolve
  114. self.compute_x_from_k()
  115. self.psi_mod_x *= self.x_evolve
  116. self.compute_k_from_x()
  117. self.psi_mod_k *= self.k_evolve
  118. self.compute_x_from_k()
  119. self.psi_mod_x *= self.x_evolve_half
  120. self.compute_k_from_x()
  121. self.t += dt * Nsteps
  122. ######################################################################
  123. # Helper functions for gaussian wave-packets
  124. def gauss_x(x, a, x0, k0):
  125. """
  126. a gaussian wave packet of width a, centered at x0, with momentum k0
  127. """
  128. return ((a * np.sqrt(np.pi)) ** (-0.5)
  129. * np.exp(-0.5 * ((x - x0) * 1. / a) ** 2 + 1j * x * k0))
  130. def gauss_k(k,a,x0,k0):
  131. """
  132. analytical fourier transform of gauss_x(x), above
  133. """
  134. return ((a / np.sqrt(np.pi))**0.5
  135. * np.exp(-0.5 * (a * (k - k0)) ** 2 - 1j * (k - k0) * x0))
  136. ######################################################################
  137. # Utility functions for running the animation
  138. def theta(x):
  139. """
  140. theta function :
  141. returns 0 if x<=0, and 1 if x>0
  142. """
  143. x = np.asarray(x)
  144. y = np.zeros(x.shape)
  145. y[x > 0] = 1.0
  146. return y
  147. def square_barrier(x, width, height):
  148. return height * (theta(x) - theta(x - width))
  149. ######################################################################
  150. # Create the animation
  151. # specify time steps and duration
  152. dt = 0.01
  153. N_steps = 50
  154. t_max = 120
  155. frames = int(t_max / float(N_steps * dt))
  156. # specify constants
  157. hbar = 1.0 # planck's constant
  158. m = 1.9 # particle mass
  159. # specify range in x coordinate
  160. N = 2 ** 11
  161. dx = 0.1
  162. x = dx * (np.arange(N) - 0.5 * N)
  163. # specify potential
  164. V0 = 1.5
  165. L = hbar / np.sqrt(2 * m * V0)
  166. a = 3 * L
  167. x0 = -60 * L
  168. V_x = square_barrier(x, a, V0)
  169. V_x[x < -98] = 1E6
  170. V_x[x > 98] = 1E6
  171. # specify initial momentum and quantities derived from it
  172. p0 = np.sqrt(2 * m * 0.2 * V0)
  173. dp2 = p0 * p0 * 1./80
  174. d = hbar / np.sqrt(2 * dp2)
  175. k0 = p0 / hbar
  176. v0 = p0 / m
  177. psi_x0 = gauss_x(x, d, x0, k0)
  178. # define the Schrodinger object which performs the calculations
  179. S = Schrodinger(x=x,
  180. psi_x0=psi_x0,
  181. V_x=V_x,
  182. hbar=hbar,
  183. m=m,
  184. k0=-28)
  185. ######################################################################
  186. # Set up plot
  187. fig = pl.figure()
  188. # plotting limits
  189. xlim = (-100, 100)
  190. klim = (-5, 5)
  191. # top axes show the x-space data
  192. ymin = 0
  193. ymax = V0
  194. ax1 = fig.add_subplot(211, xlim=xlim,
  195. ylim=(ymin - 0.2 * (ymax - ymin),
  196. ymax + 0.2 * (ymax - ymin)))
  197. psi_x_line, = ax1.plot([], [], c='r', label=r'$|\psi(x)|$')
  198. V_x_line, = ax1.plot([], [], c='k', label=r'$V(x)$')
  199. center_line = ax1.axvline(0, c='k', ls=':',
  200. label = r"$x_0 + v_0t$")
  201. title = ax1.set_title("")
  202. ax1.legend(prop=dict(size=12))
  203. ax1.set_xlabel('$x$')
  204. ax1.set_ylabel(r'$|\psi(x)|$')
  205. # bottom axes show the k-space data
  206. ymin = abs(S.psi_k).min()
  207. ymax = abs(S.psi_k).max()
  208. ax2 = fig.add_subplot(212, xlim=klim,
  209. ylim=(ymin - 0.2 * (ymax - ymin),
  210. ymax + 0.2 * (ymax - ymin)))
  211. psi_k_line, = ax2.plot([], [], c='r', label=r'$|\psi(k)|$')
  212. p0_line1 = ax2.axvline(-p0 / hbar, c='k', ls=':', label=r'$\pm p_0$')
  213. p0_line2 = ax2.axvline(p0 / hbar, c='k', ls=':')
  214. mV_line = ax2.axvline(np.sqrt(2 * V0) / hbar, c='k', ls='--',
  215. label=r'$\sqrt{2mV_0}$')
  216. ax2.legend(prop=dict(size=12))
  217. ax2.set_xlabel('$k$')
  218. ax2.set_ylabel(r'$|\psi(k)|$')
  219. V_x_line.set_data(S.x, S.V_x)
  220. ######################################################################
  221. # Animate plot
  222. def init():
  223. psi_x_line.set_data([], [])
  224. V_x_line.set_data([], [])
  225. center_line.set_data([], [])
  226. psi_k_line.set_data([], [])
  227. title.set_text("")
  228. return (psi_x_line, V_x_line, center_line, psi_k_line, title)
  229. def animate(i):
  230. S.time_step(dt, N_steps)
  231. psi_x_line.set_data(S.x, 4 * abs(S.psi_x))
  232. V_x_line.set_data(S.x, S.V_x)
  233. center_line.set_data(2 * [x0 + S.t * p0 / m], [0, 1])
  234. psi_k_line.set_data(S.k, abs(S.psi_k))
  235. title.set_text("t = %.2f" % S.t)
  236. return (psi_x_line, V_x_line, center_line, psi_k_line, title)
  237. # call the animator. blit=True means only re-draw the parts that have changed.
  238. anim = animation.FuncAnimation(fig, animate, init_func=init,
  239. frames=frames, interval=30, blit=True)
  240. # uncomment the following line to save the video in mp4 format. This
  241. # requires either mencoder or ffmpeg to be installed on your system
  242. #anim.save('schrodinger_barrier.mp4', fps=15, extra_args=['-vcodec', 'libx264'])
  243. pl.show()
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注