├── Results ├── Double Slit - SO.png ├── Single Slit - SO.png ├── Transmission - SO.png ├── 1D - Lattice Sine - SO.mp4 ├── 2D - Double Slit - SO.mp4 ├── 2D - Single Slit - SO.mp4 ├── 1D - Infinite Well - SO.mp4 ├── 1D - Lattice Delta - SO.mp4 ├── 1D - Infinite Barrier - CN.mp4 ├── 1D - Infinite Barrier - SO.mp4 ├── 1D - Tunneling Barrier - SO.mp4 └── 1D - Harmonic Oscillator - SO.mp4 ├── README.md └── Project 3 - Time dependent Schrodinger Equation.ipynb /Results/Double Slit - SO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/Double Slit - SO.png -------------------------------------------------------------------------------- /Results/Single Slit - SO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/Single Slit - SO.png -------------------------------------------------------------------------------- /Results/Transmission - SO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/Transmission - SO.png -------------------------------------------------------------------------------- /Results/1D - Lattice Sine - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/1D - Lattice Sine - SO.mp4 -------------------------------------------------------------------------------- /Results/2D - Double Slit - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/2D - Double Slit - SO.mp4 -------------------------------------------------------------------------------- /Results/2D - Single Slit - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/2D - Single Slit - SO.mp4 -------------------------------------------------------------------------------- /Results/1D - Infinite Well - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/1D - Infinite Well - SO.mp4 -------------------------------------------------------------------------------- /Results/1D - Lattice Delta - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/1D - Lattice Delta - SO.mp4 -------------------------------------------------------------------------------- /Results/1D - Infinite Barrier - CN.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/1D - Infinite Barrier - CN.mp4 -------------------------------------------------------------------------------- /Results/1D - Infinite Barrier - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/1D - Infinite Barrier - SO.mp4 -------------------------------------------------------------------------------- /Results/1D - Tunneling Barrier - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/1D - Tunneling Barrier - SO.mp4 -------------------------------------------------------------------------------- /Results/1D - Harmonic Oscillator - SO.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basnijholt/Computational-Physics-TDSE/master/Results/1D - Harmonic Oscillator - SO.mp4 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Computational-Physics-TDSE 2 | 3 | This folder contains the result for solving the Time Dependent Schrodinger Equation in 1D and 2D. All the code can be found in the Jupyter notebook, in which all the results can be made. For convenience, the main results are saved in the folder Results 4 | -------------------------------------------------------------------------------- /Project 3 - Time dependent Schrodinger Equation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Time Dependent Schrodinger Equation\n", 8 | " Marienne Faro - 4229592\n", 9 | " Tim Gebraad - 4247612\n", 10 | "\n", 11 | " AP3082D - Computational Physics - Project 3" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "This notebook contains the results for solving the time dependent Schrödinger equation in 1D and 2D for different types of potential landscapes. The methods used to solve these TDSE are Crank-Nicholson, which is basicly a dicretization of the differential equation. The other method is the Split Operator (SO) which calculates the quantum mechanical time evolution of a non-commuting Hamiltonian by a Lie, Suzuki, Trotter process, where the time evolution of the two parts of the Hamiltonian are rapidly alternated, resulting in an error that scales with the timestep squared." 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "(The units used can be derived from setting the electron mass to 1 and $\\hbar=0$, which simplifies the SE) " 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "### 1D TDSE " 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 70, 38 | "metadata": { 39 | "collapsed": true 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "import numpy as np\n", 44 | "import matplotlib\n", 45 | "import matplotlib.pyplot as plt\n", 46 | "import matplotlib.animation as animation\n", 47 | "import scipy.sparse.linalg\n", 48 | "import scipy.sparse as sparse\n", 49 | "from scipy.sparse import spdiags\n", 50 | "from scipy.sparse import csr_matrix\n", 51 | "import time" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 87, 57 | "metadata": { 58 | "collapsed": false 59 | }, 60 | "outputs": [], 61 | "source": [ 62 | "class TDSE_1D(object):\n", 63 | " def __init__(self):\n", 64 | " return\n", 65 | " \n", 66 | " def SetSpace(self, xmin, xmax, Xsteps):\n", 67 | " self.x = np.linspace(xmin,xmax,Xsteps)\n", 68 | " self.X = Xsteps\n", 69 | " self.dx = (xmax-xmin)/self.X\n", 70 | " \n", 71 | " self.k = np.fft.fftfreq(self.X, self.dx)\n", 72 | " return\n", 73 | " \n", 74 | " def SetPotential(self, V, param=None):\n", 75 | " self.V = np.zeros(self.X)\n", 76 | " if V=='Infinite Barrier':\n", 77 | " self.V[int(0.52*self.X):self.X-1] = 2**10\n", 78 | " elif V=='Tunneling Barrier':\n", 79 | " [w,h] = param\n", 80 | " self.V[int(0.52*self.X):int(0.52*self.X+w/self.dx)] = h\n", 81 | " elif V=='Infinite Well':\n", 82 | " a = param/2\n", 83 | " self.V[0:int(self.X/2-a/self.dx)] = 2**10\n", 84 | " self.V[int(self.X/2+a/self.dx):self.X-1] = 2**10\n", 85 | " elif V=='Harmonic Oscillator':\n", 86 | " k0 = param[0]\n", 87 | " self.V = k0*self.x**2\n", 88 | " elif V=='Lattice Sine':\n", 89 | " [A, k0] = param\n", 90 | " self.V = A-A*np.cos(k0*self.x)\n", 91 | " elif V=='Lattice Delta':\n", 92 | " [a, b, h] = param\n", 93 | " N = int(self.X*self.dx/b)\n", 94 | " dn = int(self.X/N)\n", 95 | " for n in range(N):\n", 96 | " self.V[n*dn:n*dn+int(a/self.dx)] = h\n", 97 | " else:\n", 98 | " self.V = V\n", 99 | " \n", 100 | " def SetInitialWave(self, k0=1, sigma=0.05):\n", 101 | " self.Psi0_k = np.exp(-(self.k-k0)**2/(2*sigma))*(np.pi*sigma)**(-1/4)\n", 102 | " self.Psi0_x = np.fft.fftshift(np.fft.ifft(self.Psi0_k, norm=\"ortho\"))\n", 103 | " return\n", 104 | " \n", 105 | " def SetupSimulation(self, dt, Tsteps): \n", 106 | " self.t = np.linspace(0, Tsteps*dt, Tsteps)\n", 107 | " self.dt = dt\n", 108 | " self.T = Tsteps\n", 109 | " \n", 110 | " self.Psi_x = np.zeros((self.T, self.X), dtype = complex)\n", 111 | " self.Psi_k = np.zeros((self.T, self.X), dtype = complex)\n", 112 | " self.Psi_x[0,:] = self.Psi0_x\n", 113 | " self.Psi_k[0,:] = self.Psi0_k\n", 114 | " return\n", 115 | " \n", 116 | " def SimulateCrankNicholson(self, dt=0.05, Tsteps=10000): \n", 117 | " self.SetupSimulation(dt, Tsteps)\n", 118 | "\n", 119 | " #Setup the Hamiltonian\n", 120 | " P = np.ones((3,self.X))\n", 121 | " P[1,:] *=-2\n", 122 | " H = -1/2*spdiags(P,[-1, 0, 1],self.X,self.X)+spdiags(self.V,0,self.X,self.X)\n", 123 | " \n", 124 | " #Setup the left and right hand side matrix multiplicator\n", 125 | " Hl = 1j/self.dt*scipy.sparse.identity(self.X)-1/2*H\n", 126 | " Hr = 1j/self.dt*scipy.sparse.identity(self.X)+1/2*H\n", 127 | " \n", 128 | " for t in range(self.T-1):\n", 129 | " self.Psi_x[t+1,:] = sparse.linalg.bicgstab(Hl, np.dot(Hr.toarray(),self.Psi_x[t,:]))[0]\n", 130 | " return \n", 131 | " \n", 132 | " def SimulateSplitOperator(self, dt=0.01, Tsteps=10000): \n", 133 | " self.SetupSimulation(dt, Tsteps)\n", 134 | " \n", 135 | " EvltPos = np.exp(-1j*self.dt*self.V) \n", 136 | " EvltMom = np.exp(-1j*self.dt*abs(self.k)**2)\n", 137 | " \n", 138 | " for t in range(self.T-1):\n", 139 | " self.Psi_k[t+1,:] = np.fft.fft (EvltPos*self.Psi_x[t ,:], norm=\"ortho\")\n", 140 | " self.Psi_x[t+1,:] = np.fft.ifft(EvltMom*self.Psi_k[t+1,:], norm=\"ortho\")\n", 141 | " return\n", 142 | " \n", 143 | " def AnimateSetup(self):\n", 144 | " self.fig, (self.axPos, self.axMom) = plt.subplots(2,1, facecolor='white')\n", 145 | " self.axPos.fill(np.concatenate(([self.x[0]],self.x,[self.x[self.X-1]])), np.concatenate(([-100],self.V,[-100])), 'k', alpha=0.8)\n", 146 | " self.linePsi_x, = self.axPos.plot(self.x, abs(self.Psi_x[0,:])**2, 'r')\n", 147 | " self.linePsi_k, = self.axMom.plot(self.k, abs(self.Psi_k[0,:])**2, 'y')\n", 148 | " self.axPos.set_ylim([np.min(self.V), 25])\n", 149 | " self.axPos.set_xlim([-20, 20])\n", 150 | " self.axPos.set_xlabel('$x$')\n", 151 | " self.axPos.set_ylabel(r'$|\\psi(x)| , V(x)$')\n", 152 | " self.axMom.set_xlabel('$k$')\n", 153 | " self.axMom.set_ylabel(r'$|\\psi(k)|$')\n", 154 | " self.axPos.legend(('$|\\psi(x)|$', '$V(x)$'))\n", 155 | " self.axMom.legend(('$|\\psi(k)|$',))\n", 156 | " self.text = plt.text(-5, 2.7, \"\") \n", 157 | " return\n", 158 | " \n", 159 | " def AnimationStep(self, i):\n", 160 | " self.linePsi_x.set_ydata(abs((self.Psi_x[i%self.T,:]))**2)\n", 161 | " self.linePsi_k.set_ydata(abs((self.Psi_k[i%self.T,:]))**2)\n", 162 | " self.text.set_text(\"t = %.2f\" % self.t[i%self.T])\n", 163 | " return (self.linePsi_x, self.linePsi_k, self.text)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "First set up the 1D TDSE object, with the appropriate space and initial Gaussian Wave" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 88, 176 | "metadata": { 177 | "collapsed": false 178 | }, 179 | "outputs": [], 180 | "source": [ 181 | "MyTDSE = TDSE_1D()\n", 182 | "MyTDSE.SetSpace(xmin=-100, xmax=100, Xsteps=2001)\n", 183 | "MyTDSE.SetInitialWave(k0=4, sigma=0.05)" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "First look at an inifinite barriere, where the Gaussian wave reflects for Crank-Nicholson and the Split operator method" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 73, 196 | "metadata": { 197 | "collapsed": false 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "MyTDSE.SetPotential('Infinite Barrier')\n", 202 | "MyTDSE.SimulateCrankNicholson(Tsteps=3000)\n", 203 | "MyTDSE.AnimateSetup()\n", 204 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 205 | "plt.show()" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 74, 211 | "metadata": { 212 | "collapsed": false 213 | }, 214 | "outputs": [], 215 | "source": [ 216 | "MyTDSE.SimulateSplitOperator(Tsteps=3000)\n", 217 | "MyTDSE.AnimateSetup()\n", 218 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 219 | "plt.show()" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "Now for a tunneling barrier, classicly a wave cannot pass this barrier if it's to heigh ($V(x)>\\frac{1}{2}p^2$), but quantum mechanically there is a possibility of tunneling (transmitting). This possibility can be looked at by varying the initial velocity of the wave or the potential barrier. From now on the Split Operator method is used, because it turns out to be computationally faster." 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 9, 232 | "metadata": { 233 | "collapsed": false 234 | }, 235 | "outputs": [], 236 | "source": [ 237 | "fig, (ax1, ax2, ax3) = plt.subplots(1,3, facecolor='white')\n", 238 | "ax = (ax1, ax2, ax3)\n", 239 | "\n", 240 | "#Set ranges for the width (A), wavenumbers (K) and height(V) that are analyzed\n", 241 | "A = [0.1, 0.5, 1.0]\n", 242 | "K = np.arange(0.0, 4, 0.2)\n", 243 | "V = np.array([0.1, 3, 7, 10])\n", 244 | "c= ['r', 'g', 'b', 'm']\n", 245 | "T = np.zeros((len(K), len(V)))\n", 246 | "\n", 247 | "#Perform the simulation and obtain the tranmission for all combinations\n", 248 | "for n, a in enumerate(A):\n", 249 | " for i, k in enumerate(K):\n", 250 | " MyTDSE.SetInitialWave(k0=k, sigma=0.05)\n", 251 | " for j, v in enumerate(V):\n", 252 | " MyTDSE.SetPotential('Tunneling Barrier', [a, v])\n", 253 | " MyTDSE.SimulateSplitOperator(Tsteps=3000)\n", 254 | " T[i,j] = np.sum(abs(MyTDSE.Psi_x[-1, int(0.52*MyTDSE.X):-1])**2)/np.sum(abs(MyTDSE.Psi_x[-1])**2)\n", 255 | "\n", 256 | " for j, v in enumerate(V):\n", 257 | " ax[n].plot(K, T[:, j], color=c[j], marker='*', markeredgecolor=c[j])\n", 258 | " \n", 259 | " #Plot tweaking\n", 260 | " ax[n].set_xlabel('$k_0$')\n", 261 | " ax[n].set_ylabel('$Transmission$')\n", 262 | " ax[n].set_title('Width ='+ str(a))\n", 263 | " if n==0:\n", 264 | " ax[n].legend(('$V_{max} = 0.1$', '$V_{max} = 3$', '$V_{max} = 7$', '$V_{max} = 10$'))\n", 265 | " \n", 266 | " #Plot the classical boundary and the theoretical (quantum) expectation value\n", 267 | " for j, v in enumerate(V):\n", 268 | " ax[n].plot(np.ones(2)*np.sqrt(V[j]), [0,1], lw=5, color=c[j], alpha=0.3)\n", 269 | " k1 = np.lib.scimath.sqrt(K**2-v*2)\n", 270 | " Tth = 4*K*k1*np.exp(-1j*a*(K-k1))/((K+k1)**2-np.exp(2j*a*k1)*(K-k1)**2)\n", 271 | " ax[n].plot(K, abs(Tth)**2, color=c[j], ls=':')\n", 272 | "plt.show()" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "\n", 280 | "\n", 281 | "The tranmission shows the expected shape when looking at the theoretical expectation (dotted), but it does differ quite a lot from it (we expect this to be a mistake with a constant). It is also seen that the tranmission indeed is non-zero in the quantum mechanical case, whereas this is classically (vertical lines) not possible." 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "metadata": {}, 287 | "source": [ 288 | "Next, two confining potentials are implemented: the infinite potential well and the harmonic oscillator." 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": 76, 294 | "metadata": { 295 | "collapsed": false 296 | }, 297 | "outputs": [], 298 | "source": [ 299 | "#Set up the appropriate energy for level n and well width a and give this as input to the TDSE\n", 300 | "n = 1\n", 301 | "a = 5\n", 302 | "MyTDSE.SetInitialWave(k0=n*np.pi/a, sigma=0.001)\n", 303 | "MyTDSE.SetPotential('Infinite Well', a)\n", 304 | "MyTDSE.SimulateSplitOperator(Tsteps=10000)\n", 305 | "MyTDSE.AnimateSetup()\n", 306 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 307 | "plt.show()" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 77, 313 | "metadata": { 314 | "collapsed": false 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "#Set up the appropriate energy level n and oscillator wavenumer k1 and give this as input to the TDSE\n", 319 | "n = 1\n", 320 | "k1 = 1\n", 321 | "\n", 322 | "\n", 323 | "MyTDSE.SetInitialWave(k0=k1*np.sqrt(n+0.5), sigma=0.001)\n", 324 | "MyTDSE.SetPotential('Harmonic Oscillator', [k1])\n", 325 | "MyTDSE.SimulateSplitOperator(Tsteps=10000)\n", 326 | "MyTDSE.AnimateSetup()\n", 327 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 328 | "plt.show()" 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": {}, 334 | "source": [ 335 | "We have runned simulations where we wanted to find the quantized levels of both the infinite well potential and the harmonic oscillator, by inserting the right energy via k. This was however unsuccesfull, maybe due to non-relaxation of the system or maybe due to other reasons." 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": {}, 341 | "source": [ 342 | "Lastly, two periodic potentials are looked at. Firstly a simple sine potential and secondly a potential with delta peaks. Tweaking the parameters of the latter can result in a potential that resembles the one in a crystallic structure, so for example the behavoir of an electron within a crystal lattice can be studied (and compared to the free electron)." 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": 78, 348 | "metadata": { 349 | "collapsed": true 350 | }, 351 | "outputs": [], 352 | "source": [ 353 | "MyTDSE.SetInitialWave(k0=2, sigma=0.05)" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 79, 359 | "metadata": { 360 | "collapsed": true 361 | }, 362 | "outputs": [], 363 | "source": [ 364 | "MyTDSE.SetPotential('Lattice Sine', [1, 1])\n", 365 | "MyTDSE.SimulateSplitOperator(Tsteps=5000)\n", 366 | "MyTDSE.AnimateSetup()\n", 367 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 368 | "plt.show()" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 80, 374 | "metadata": { 375 | "collapsed": false 376 | }, 377 | "outputs": [], 378 | "source": [ 379 | "MyTDSE.SetPotential('Lattice Delta', [0.1, 2, -2])\n", 380 | "MyTDSE.SimulateSplitOperator(Tsteps=5000)\n", 381 | "MyTDSE.AnimateSetup()\n", 382 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 383 | "plt.show()" 384 | ] 385 | }, 386 | { 387 | "cell_type": "markdown", 388 | "metadata": { 389 | "collapsed": true 390 | }, 391 | "source": [ 392 | "### TDSE 2D" 393 | ] 394 | }, 395 | { 396 | "cell_type": "markdown", 397 | "metadata": {}, 398 | "source": [ 399 | "And now for 2D. We tried to implement this for both the Crank-Nicholson and Split Operator method, but for CN we did not get the wave moving, so the experiments have been run with SO.\n", 400 | "To create a plane wave we have used three boundary conditions:\n", 401 | "- At the bottom (our case y=-4) the wave is forces to be:\n", 402 | " - $ \\psi (x) = e^{i(kx-\\omega t)}$\n", 403 | " - $ \\frac{d\\psi (x)}{dx} = ike^{i(kx-\\omega t)}$\n", 404 | "- And additionally, in order to avoid periodic boundary effects of the Fourier Transform at the top:\n", 405 | " - $ \\psi (x) = 0$" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": 81, 411 | "metadata": { 412 | "collapsed": true 413 | }, 414 | "outputs": [], 415 | "source": [ 416 | "class TDSE_2D(object):\n", 417 | " def __init__(self):\n", 418 | " return\n", 419 | " \n", 420 | " def SetSpace(self, xmin, xmax, Xsteps, ymin, ymax, Ysteps):\n", 421 | " self.x = np.linspace(xmin, xmax, Xsteps)\n", 422 | " self.X = Xsteps\n", 423 | " self.dx= (xmax-xmin)/self.X\n", 424 | " \n", 425 | " self.y = np.linspace(ymin, ymax, Ysteps)\n", 426 | " self.Y = Ysteps\n", 427 | " self.dy= (ymax-ymin)/self.Y\n", 428 | " \n", 429 | " self.kx = np.fft.fftfreq(self.X, self.dx)\n", 430 | " self.ky = np.fft.fftfreq(self.Y, self.dy)\n", 431 | " \n", 432 | " xx, yy = np.meshgrid(self.kx, self.ky)\n", 433 | " self.k = np.sqrt(xx**2 + yy**2)\n", 434 | " return\n", 435 | " \n", 436 | " def SetPotential(self, V):\n", 437 | " self.V = np.zeros((self.X, self.Y), dtype=complex)\n", 438 | " if V=='Empty':\n", 439 | " return\n", 440 | " if V=='Single Slit':\n", 441 | " self.V[int(0.15*self.X):int(0.16*self.X), int(0.00*self.Y):int(0.45*self.Y)] = 2**10\n", 442 | " self.V[int(0.15*self.X):int(0.16*self.X), int(0.55*self.Y):int(1.00*self.Y)] = 2**10\n", 443 | " elif V=='Double Slit':\n", 444 | " self.V[int(0.15*self.X):int(0.16*self.X), int(0.00*self.Y):int(0.40*self.Y)] = 2**10\n", 445 | " self.V[int(0.15*self.X):int(0.16*self.X), int(0.45*self.Y):int(0.55*self.Y)] = 2**10\n", 446 | " self.V[int(0.15*self.X):int(0.16*self.X), int(0.60*self.Y):int(1.00*self.Y)] = 2**10\n", 447 | " else:\n", 448 | " self.V = V\n", 449 | " \n", 450 | " def SetupSimulation(self, dt, Tsteps):\n", 451 | " self.t = np.linspace(0, (Tsteps-1)*dt, Tsteps)\n", 452 | " self.dt = dt\n", 453 | " self.T = Tsteps\n", 454 | " \n", 455 | " self.Psi_x = np.zeros((self.T, self.X, self.Y), dtype = complex)\n", 456 | " self.Psi_k = np.zeros((self.T, self.X, self.Y), dtype = complex)\n", 457 | " return\n", 458 | " \n", 459 | " def SimulateCrankNicolson(self, dt=0.01, Tsteps=1001):\n", 460 | " self.SetupSimulation(dt, Tsteps)\n", 461 | " \n", 462 | " #Set up the Hamiltonian\n", 463 | " P = np.ones((3,self.X))\n", 464 | " P[1,:] *=-2\n", 465 | " H = scipy.sparse.identity(self.X*self.Y)\n", 466 | " P = -np.ones(self.X*self.Y)\n", 467 | " for i in range (self.X-1):\n", 468 | " P[self.X*(i+1)] = 0\n", 469 | " H+= spdiags([np.concatenate((P[1:len(P)], [0])),P], [-1, 1], self.X*self.Y, self.X*self.Y)\n", 470 | " H+= spdiags(np.ones((2,self.X*self.Y)), [-self.X, self.X], self.X*self.Y, self.X*self.Y)\n", 471 | " H+= spdiags(np.reshape(self.V, (self.X*self.Y)),0, self.X*self.Y,self.X*self.Y)\n", 472 | " \n", 473 | " Hr = 2j/self.dt*scipy.sparse.identity(self.X*self.Y)-1/2*H\n", 474 | " Hl = 2j/self.dt*scipy.sparse.identity(self.X*self.Y)+1/2*H\n", 475 | " \n", 476 | " k0=1\n", 477 | " self.Psi_x[0] = np.zeros((self.X, self.Y), dtype=complex) \n", 478 | " \n", 479 | " for t in range(self.T-1):\n", 480 | " print('Simulated timestep', self.t[t])\n", 481 | " \n", 482 | " #Use boundary conditions\n", 483 | " self.Psi_x[t, 0] = np.ones(self.Y)*np.exp(-1j*self.t[t]*np.pi)\n", 484 | " self.Psi_x[t, 1] = self.Psi_x[t, 0, :] + self.dx*np.ones(self.Y)*np.exp(-1j*self.t[t]*np.pi)*1j*k0\n", 485 | " self.Psi_x[t,-1] = 0\n", 486 | " \n", 487 | " #Calculate the next step\n", 488 | " Psi = np.reshape(self.Psi_x[t,:,:], (self.X*self.Y))\n", 489 | " Psi = sparse.linalg.bicgstab(Hl, Hr.dot(Psi))[0]\n", 490 | " self.Psi_x[t+1,:,:] = np.reshape(Psi, (self.X, self.Y)) \n", 491 | "\n", 492 | " return\n", 493 | " \n", 494 | " def SimulateSplitOperator(self, dt=0.01, Tsteps=1001): \n", 495 | " self.SetupSimulation(dt, Tsteps)\n", 496 | "\n", 497 | " EvltPos = np.exp(-1j*self.dt*self.V)\n", 498 | " EvltMom = np.exp(-1j*self.dt*abs(self.k)**2).transpose()\n", 499 | " \n", 500 | " k0 = 1\n", 501 | " self.Psi_x[0] = np.zeros((self.X, self.Y), dtype=complex) \n", 502 | " \n", 503 | " for t in range(self.T-1):\n", 504 | " #Use boundary conditions\n", 505 | " self.Psi_x[t, 0] = np.ones(self.Y)*np.exp(-1j*self.t[t]*np.pi)\n", 506 | " self.Psi_x[t, 1] = self.Psi_x[t, 0, :] + self.dx*np.ones(self.Y)*np.exp(-1j*self.t[t]*np.pi)*1j*k0\n", 507 | " self.Psi_x[t,-1] = 0\n", 508 | " \n", 509 | " #Calculate the next step\n", 510 | " self.Psi_k[t+1] = np.fft.fft2 (EvltPos*self.Psi_x[t ], norm=\"ortho\")\n", 511 | " self.Psi_x[t+1] = np.fft.ifft2(EvltMom*self.Psi_k[t+1], norm=\"ortho\")\n", 512 | " return\n", 513 | " \n", 514 | " def AnimateSetup(self, interval=10):\n", 515 | " self.fig, (self.ax1D, self.ax2D) = plt.subplots(1,2)\n", 516 | " self.ax2D.pcolormesh(self.x,self.y,np.real(self.V),cmap='Greys')\n", 517 | " self.quadPsi_x = self.ax2D.pcolormesh(self.x, self.y, abs(self.Psi_x[0])**2, shading='gouraud', alpha=0.1, cmap='plasma')\n", 518 | " \n", 519 | " self.linePsi_x, = self.ax1D.plot(self.y, abs(self.Psi_x[0,:,0])**2, 'y') \n", 520 | " \n", 521 | " self.ax2D.set_xlabel('$x$')\n", 522 | " self.ax2D.set_ylabel('$y$')\n", 523 | " self.ax2D.set_title('Position')\n", 524 | " self.ax1D.set_xlabel('$x$')\n", 525 | " self.ax1D.set_ylabel(r'$|\\psi(x)|$')\n", 526 | " self.ax1D.set_title('Pattern at $y=-2$')\n", 527 | " \n", 528 | " self.ax1D.set_xlim([-4, 4])\n", 529 | " self.ax1D.set_ylim([0, 2])\n", 530 | " self.ax2D.set_ylim([-4, -2])\n", 531 | " self.text = plt.text(2.5, -2.5, \"\") \n", 532 | " \n", 533 | " cbar = self.fig.colorbar(self.quadPsi_x, ticks=[-1, 0, 1])\n", 534 | " return\n", 535 | " \n", 536 | " def AnimationStep(self, i): \n", 537 | " self.linePsi_x.set_ydata(abs((self.Psi_x[i%self.T,int(0.25*self.X)]))**2)\n", 538 | " self.quadPsi_x.set_array(np.ravel(abs((self.Psi_x[i%self.T,:,:]))**2))\n", 539 | " self.text.set_text(\"t = %.2f\" % self.t[i%self.T])\n", 540 | " return (self.linePsi_x, self.quadPsi_x, self.text)" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": 82, 546 | "metadata": { 547 | "collapsed": false 548 | }, 549 | "outputs": [], 550 | "source": [ 551 | "MyTDSE = TDSE_2D()\n", 552 | "MyTDSE.SetSpace(xmin=-4, xmax=4, Xsteps=2**7, ymin=-4, ymax=4, Ysteps=2**7)" 553 | ] 554 | }, 555 | { 556 | "cell_type": "code", 557 | "execution_count": 83, 558 | "metadata": { 559 | "collapsed": false 560 | }, 561 | "outputs": [], 562 | "source": [ 563 | "MyTDSE.SetPotential('Single Slit')\n", 564 | "MyTDSE.SimulateSplitOperator(dt=0.01, Tsteps=1000)\n", 565 | "MyTDSE.AnimateSetup(interval=10)\n", 566 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 567 | "plt.show()" 568 | ] 569 | }, 570 | { 571 | "cell_type": "markdown", 572 | "metadata": {}, 573 | "source": [ 574 | "The results (SO), including the diffraction pattern for a single slit experiment:\n", 575 | "\n", 576 | "\n", 577 | " \n", 578 | "This diffraction is as can be expected by theory (qualitively)." 579 | ] 580 | }, 581 | { 582 | "cell_type": "code", 583 | "execution_count": 84, 584 | "metadata": { 585 | "collapsed": true 586 | }, 587 | "outputs": [], 588 | "source": [ 589 | "MyTDSE.SetPotential('Double Slit')\n", 590 | "MyTDSE.SimulateSplitOperator(dt=0.01, Tsteps=1000)\n", 591 | "MyTDSE.AnimateSetup(interval=10)\n", 592 | "ani = animation.FuncAnimation(MyTDSE.fig, MyTDSE.AnimationStep, interval=10, blit=True)\n", 593 | "plt.show()" 594 | ] 595 | }, 596 | { 597 | "cell_type": "markdown", 598 | "metadata": {}, 599 | "source": [ 600 | "And for the double slit:\n", 601 | "\n", 602 | "\n", 603 | "\n", 604 | "This diffraction is as can be expected by theory (qualitively)." 605 | ] 606 | }, 607 | { 608 | "cell_type": "code", 609 | "execution_count": null, 610 | "metadata": { 611 | "collapsed": true 612 | }, 613 | "outputs": [], 614 | "source": [] 615 | } 616 | ], 617 | "metadata": { 618 | "kernelspec": { 619 | "display_name": "Python 3", 620 | "language": "python", 621 | "name": "python3" 622 | }, 623 | "language_info": { 624 | "codemirror_mode": { 625 | "name": "ipython", 626 | "version": 3 627 | }, 628 | "file_extension": ".py", 629 | "mimetype": "text/x-python", 630 | "name": "python", 631 | "nbconvert_exporter": "python", 632 | "pygments_lexer": "ipython3", 633 | "version": "3.5.1" 634 | } 635 | }, 636 | "nbformat": 4, 637 | "nbformat_minor": 0 638 | } 639 | --------------------------------------------------------------------------------