tunnelling

Tunnelling

In quantum mechanics, the wavefunction of a particle can appear in places where a classical particle would never be found. For instance, a portion of the wavefunction can appear on the other side of a barrier which is impassible to a classical particle. This occurs in quantum mechanics due to the uncertainty in the position of the wavefunction.

The simulation below shows how the probability distribution of a Gaussian wavefunction changes in time according to a Hamiltonian with the potential

Potential

where a is the height of the barrier in the centre and b is its width. These two parameters can be adjusted in order to observe how they determine the behaviour of the wavefunction.

The units used here are dimensionless, with ℏ=m=1.

Barrier Width:
Barrier Height:

Elise Kenny
2016

Code Samples

Matlab
barrierWidth = 0.12;
barrierHeight = 2.1;

xmax = 10;
dt = 1.0e-2;                    % Time step size
N = 512;                        % Simulation spatial resolution
x = (-N/2:N/2-1)*2*xmax/N;      % Position axis
p = (-N/2:N/2-1)*2*pi/xmax;     % Momentum axis

% Calculate potential
potential = 0.25*x.^2 + barrierHeight*exp(-(x/barrierWidth).^2);

% Calculate initial wavefunction
wavefunction = sqrt(sqrt(1/(2*pi)))*exp(-0.25*(x+4).^2);

% Calculate the propogators
kprop = exp(-1i*dt*p.^2);
xprop = exp(-1i*dt*potential);

% Setup plot
figure;
[ax, hp, hw] = plotyy(x, potential, x, abs(wavefunction).^2);
axis(ax(1), [-xmax xmax 0 25]); set(ax(1), 'Ytick', 0:5:25);
axis(ax(2), [-xmax xmax 0 1]); set(ax(2), 'Ytick', 0:0.1:1);
legend('Potential', '|Wavefunction|^2');
xlabel('Position');

% Evolve the system and show the animation
while ishandle(hw)
    
    % Evolve the wavefunction
    kphi = fftshift(fft(fftshift(wavefunction))).*kprop;
    wavefunction = fftshift(ifft(fftshift(kphi))).*xprop;
    
    set(hw, 'ydata', abs(wavefunction).^2);
    pause(0.05);
    drawnow;
end
Python
import numpy as np
from numpy import fft
import matplotlib.pyplot as plt
import matplotlib.animation as animation

barrierWidth = 0.12;
barrierHeight = 2.1;
animLength = 1000;

xmax = 10;
dt = 1.0e-2;                            # Time step size
N = 512;                                # Simulation spatial resolution
x = np.arange(-N/2, N/2)*2.0*xmax/N;      # Position axis
p = np.arange(-N/2, N/2)*2.0*np.pi/xmax;  # Momentum axis

# Calculate potential
potential = 0.25*x**2 + barrierHeight*np.exp(-(x/barrierWidth)**2.0);

# Calculate initial wave-function
wavefunction = np.sqrt(np.sqrt(1/(2.0*np.pi)))*np.exp(-0.25*(x+4)**2.0);

# Calculate the propagators
kprop = np.exp(-1j*dt*p**2);
xprop = np.exp(-1j*dt*potential);

# Setup plot
fig, ax1 = plt.subplots()
line1, = ax1.plot(x, potential, 'b')
ax1.set_ylim(0.0, 25.0)
ax1.set_xlim(-xmax, xmax)
ax1.set_xlabel("Position")

ax2 = ax1.twinx()
line2, = ax2.plot(x, np.abs(wavefunction)**2.0, 'r')
ax2.set_ylim(0.0, 1.0)

fig.legend((line1, line2), ('Potential', '|Wavefunction|^2'))

def animate(step_number):

    global wavefunction

    # Evolve the wave-function
    kphi = fft.fftshift(fft.fft(fft.fftshift(wavefunction)))*kprop;
    wavefunction = fft.fftshift(fft.ifft(fft.fftshift(kphi)))*xprop;

    # Update the plot
    line2.set_data(x, np.abs(wavefunction)**2.0)
    return line2,

ani = animation.FuncAnimation(fig, animate, xrange(1, animLength),
    interval=25, blit=True, repeat=False)
#ani.save('output.mp4')
plt.show()
Cpp Gnuplot
#include <fstream>
#include <vector>
#include <complex>
#include <cmath>
#include <fftw3.h>      // For Fourier transforms, link with -lfftw3
using std::complex;

const double barrierWidth = 0.12;           // Width of barrier
const double barrierHeight = 2.1;           // Height of barrier
const unsigned animLength = 1000;           // Number of output frames (files)

const double xmax = 10.0;                   // Half width of the simulation
const double dt = 1.0e-2;                   // Time step size
const unsigned N = 512;                     // Simulation spatial resolution

// Implementation of Matlab's fftshift function
void fftshift(std::vector<complex<double>>& vec) {
  std::vector<complex<double>>::iterator mid = vec.begin() + vec.size()/2;
  std::swap_ranges(vec.begin(), mid, mid);
}

int main(int argc, char** argv) {

  std::vector<complex<double>> kprop(N);
  std::vector<complex<double>> xprop(N);
  std::vector<complex<double>> wavefunction(N);
  std::vector<complex<double>> pwavefunction(N);
  std::complex<double> cJ(0.0, 1.0);

  // Create a plan for the Fourier transforms
  fftw_plan forward, backward;
  forward = fftw_plan_dft_1d(N, (double(*)[2]) wavefunction.data(),
      (double(*)[2]) pwavefunction.data(), FFTW_FORWARD, FFTW_ESTIMATE);
  backward = fftw_plan_dft_1d(N, (double(*)[2]) pwavefunction.data(),
      (double(*)[2]) wavefunction.data(), FFTW_BACKWARD, FFTW_ESTIMATE);

  std::ofstream fpp("potential.dat");
  fpp << "# x\tPotential\n";

  for (unsigned i = 0; i < N; ++i) {
    double x = 2*i*xmax/N - xmax;
    double p = 2*M_PI*i/xmax - N*M_PI/xmax;

    double potential = 0.25*x*x + barrierHeight*exp(-pow(x/barrierWidth, 2.0));

    // Calculate the propagators
    kprop[i] = exp(-cJ*dt*p*p)/pow(N, 0.5);
    xprop[i] = exp(-cJ*dt*potential)/pow(N, 0.5);

    // Calculate initial wave-function
    wavefunction[i] = sqrt(sqrt(1.0/(2.0*M_PI)))*exp(-0.25*pow(x+4.0, 2.0));

    // Write the potential to file
    fpp << x << '\t' << potential << '\n';
  }
  fpp.close();
  fftshift(kprop);

  for (unsigned i = 0; i < animLength; ++i) {
    // Evolve the wave-function
    fftw_execute(forward);
    for (unsigned k = 0; k < N; ++k) pwavefunction[k] *= kprop[k];
    fftw_execute(backward);
    for (unsigned k = 0; k < N; ++k) wavefunction[k] *= xprop[k];

    // Write the wave-function to file
    char buf[128];
    snprintf(buf, 128, "output_%06d.dat", i);
    std::ofstream fp(buf);
    fp << "# x\t|Wavefunction|^2\n";
    for (unsigned k = 0; k < N; ++k) {
      double x = 2*k*xmax/N - xmax;
      fp << x << '\t' << pow(abs(wavefunction[k]), 2.0) << '\n';
    }
  }

  // Cleanup the plans
  fftw_destroy_plan(forward);
  fftw_destroy_plan(backward);

  return 0;
}
set terminal gif animate delay 10
set output 'output.gif'
set xrange [-10:10]
set x2range [-10:10]
set yrange [0.0:25.0]
set y2range [0.0:512*512.0]
set xlabel "Position"

do for [i=0:1] {
    plot "potential.dat" with lines title "Potential" axes x1y1, \
      sprintf("output_%06d.dat", i) with lines \
        title sprintf("|Wavefunction|^2 (at %d)", i) axes x2y2
}