Abstract
In a 1997 paper Moore and Schroeder argued that the development of student understanding of thermal physics could be enhanced by computational exercises that highlight the link between the statistical definition of entropy and the second law of thermodynamics [Am. J. Phys. 65, 26 (1997)]. I introduce examples of similar computational exercises for systems in which the quantum statistics of identical particles plays an important role. I treat isolated systems of small numbers of particles confined in a common harmonic potential, and use a computer to enumerate all possible occupation-number configurations and multiplicities. The examples illustrate the effect of quantum statistics on the sharing of energy between weakly interacting subsystems, as well as the distribution of energy within subsystems. The examples also highlight the onset of Bose-Einstein condensation in small systems.
In Section II.C I calculate the
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from sympy.utilities.iterables import partitions
from scipy import special
# Following is an Ipython magic command that puts figures in notebook.
%matplotlib notebook
# M.L. modifications of matplotlib defaults
# Changes can also be put in matplotlibrc file,
# or effected using mpl.rcParams[]
mpl.style.use('classic')
plt.rc('figure', figsize = (6, 4.5)) # Reduces overall size of figures
plt.rc('axes', labelsize=16, titlesize=14)
plt.rc('figure', autolayout = True) # Adjusts supblot parameters for new size
def convert(npa):
'''Converts numpy array with information on occupation-number configuration for
for bosons to correspoding occupation-number configuration for fermions.
See Appendix A of manuscript.'''
n = np.sum(npa[1])
count = 0
for i in range(len(npa[0])):
for j in range(npa[1,i]):
#print(i, j, count)
base[n_particles - 1 - count], base[n_particles- 1 - count + npa[0,i]]\
= base[n_particles - 1 - count + npa[0,i]], base[n_particles - 1 - count]
count += 1
return(base)
def bose_einstein(q,t):
'''Bose-Einstein distribution for n bosons in 1-D HO potential (with
energy spacing of 1) at temperature t in the thermodynamic limit. The
constant c depends on the number of particles; it was determined using
distributions_limit.ippynb '''
c = 1.10109
return 1/(c*np.exp(q/t)-1)
def fermi_dirac(q,t):
'''Fermi-Dirac distribution for n fermions in 1-D HO potential (with
energy spacing of 1) at temperature t in the thermodynamic limit. The
constant c depends on the number of particles; it was determined using
distributions_limit.ippynb '''
c = 0.03495
return 1/(c*np.exp(q/t)+1)
def boltzmann(q,t):
'''Boltzmann distribution for n distinguishablee particles in 1-D
HO potential (with energy spacing of 1) at temperature t in the
thermodynamic limit. The constant c depends on the number of
particles; it was determined using distributions_limit.ippynb '''
return 1/(0.31372*np.exp(q/t))
Uses Eq. (11) of manuscript.
n_particles = 20
energy_total = 40
t = 5.76
sum = np.zeros(energy_total + 1)
count = 0
for p in partitions(energy_total, n_particles):
npa = np.array(list(p.items())).T
for i in range(len(npa[0])):
sum[npa[0,i]] += npa[1,i]
count += 1
#print(count)
pop_B = sum/count
pop_B[0] = n_particles - np.sum(pop_B)
plt.figure()
x = np.linspace(0,len(num)-1,len(num))
xc = np.linspace(0,len(num)-1, 201)
plt.scatter(x,pop_B)
plt.plot(xc, bose_einstein(xc,t))
plt.xlim(-0.5,40)
plt.ylim(0,9.5)
plt.grid()
plt.xlabel('energy level, $m$')
plt.ylabel('population, $n_m$')
plt.title('bosons')
plt.axhline(0)
plt.axvline(0);
n_particles = 20
eF = int(n_particles*(n_particles -1)/2)
energy_total = 40 + eF
energy_total, eF
count = 0
sum = np.zeros(n_particles + energy_total-eF)
for p in partitions(energy_total - eF, n_particles):
base = np.append(np.ones(n_particles), np.zeros(energy_total - eF))
npa = np.array(list(p.items())).T
sum += convert(npa)
count += 1
pop_F = sum/count
plt.figure()
x = np.linspace(0,len(pop_F)-1,len(pop_F))
xc = np.linspace(0,len(pop_F)-1, 201)
plt.scatter(x,pop_F)
plt.plot(xc,fermi_dirac(xc,t))
plt.xlim(0,40)
plt.ylim(0,4)
plt.grid()
plt.xlabel('energy level, $m$')
plt.ylabel('population, $n_m$')
plt.title('fermions')
plt.axhline(0)
plt.axvline(0);
NOTE 1:Uses Eq. (11) of manuscript, but algorithm for calculation of multiplicity not discussed there.
Illustration of algorithm -- Consider an occupation-number configuration of 9 particles with {$n_0 = 2, n_1=4, n_2=0, n_3=3$}. Consider the 3 particles with energy $3\epsilon$ first. There are $_3^9\mbox{C} = 84$ ways too chose the particles that have energy $3\epsilon$. After choosing the first three particles, there are $9-3$ particles left, and the there are $_4^6\mbox{C} = 15$ ways to chose the particles that have energy $\epsilon$. There is no remaining choice in the particles that have energy 0. So
$$ \Omega_D = _4^9\mbox{C}\times\, _2^{9-3}\mbox{C} = 84\times 15 = 1260. $$NOTE 2: To achieve a temperature for distinguishable particles that is the same as that of the boson and fermion illustrations above requires an energy of $99\epsilon$. The calculation of the distribution in this case requires much more time than the calculation for for bosons and fermions.
NOTE 3: This calculation takes a long time.
%%time
energy_total = 99
sum = np.zeros(energy_total + 1)
count = 0
for p in partitions(energy_total, n_particles):
npa = np.array(list(p.items())).T
g = 1
nn = n_particles
for i in range(len(npa[0])):
g *= special.binom(nn, npa[1,i])
nn -= npa[1,i]
count += g
for i in range(len(npa[0])):
sum[npa[0,i]] += npa[1,i]*g
#print(count)
pop_D = sum/count
pop_D[0] = n_particles - np.sum(pop_D)
plt.figure()
x = np.linspace(0, energy_total, energy_total+1)
xc = np.linspace(0, energy_total, 201)
y = pop_D
y2 = boltzmann(xc,t)
plt.scatter(x,y)
plt.plot(xc,y2,color='red')
plt.xlim(-0.5,40)
plt.grid()
plt.xlabel('energy level, $m$')
plt.ylabel('population, $n_m$')
plt.title('distinguishable')
plt.axhline(0)
plt.axvline(0);
energy_total = 40
plt.figure()
x = np.linspace(0, energy_total, energy_total+1)
xc = np.linspace(0, energy_total, energy_total+1)
yD = pop_D[:41]
yB = pop_B
yF = pop_F[:41]
y2 = boltzmann(xc, t)
y3 = bose_einstein(xc, t)
y4 = fermi_dirac(xc, t)
plt.scatter(x,yD, label='distinguishable', color='blue')
plt.scatter(x,yB, label='bosons', color='green')
plt.scatter(x,yF, label='fermions', color='red')
plt.plot(xc,y2)
plt.plot(xc,y3)
plt.plot(xc,y4)
plt.xlim(-0.5,40)
plt.ylim(0,4.2)
plt.grid()
plt.xlabel('energy level, $m$')
plt.ylabel('population, $n_m$')
plt.title('$n = 20$, $kT = 5.76\epsilon$')
plt.axhline(0)
plt.axvline(0)
plt.legend();
version_information
is from J.R. Johansson (jrjohansson at gmail.com
); see Introduction to scientific computing with Python for more information and instructions for package installation.
%load_ext version_information
version_information numpy, scipy, sympy, matplotlib