Chapter 2: Python Programming for physicists

Exercises are from "Computational Physics" by Mark Newman.
Programmed by Sasha Bakker.

In [27]:
from numpy import *

Exercise 2.1: Another ball dropped from a tower

A ball is again dropped from a tower of height h with initial velocity zero.

In [38]:
h = 100.0
g = 9.81 # m/s**2
vi = 0 # m/s
vf = sqrt(vi**2+2*g*h)
t = (vf-vi)/g
print(f"Tower height (m): {h}")
print(f"Ball drop time (s): {round(t,3)}")
Tower height (m): 100.0
Ball drop time (s): 4.515

Exercise 2.2: Altitude of a satellite

A satellite is to be launched into a circular orbit around the Earth so that it orbits the planet once every T seconds.
The motion can be expressed by Kepler's Law of Periods:

$h=\left(\frac{GMT^{2}}{4\pi^{2}}\right)^{1/3}-R$

Where G is Newton's gravitational constant, M is the mass of Earth, and R is its radius.

In [40]:
G = 6.67*10**-11 # m**3 kg**-1 s**-2
M = 5.97*10**24 # kg 
R = 6371*1000 # m
periods = [86400, 86148, 5400, 2700] # s
for T in periods:
    h = ((G*M*T**2)/(4*pi**2))**(1/3)-R
    print(f"For a period of {T} seconds, the satellite altitude is {round(h/1000,3)} km.")
     
For a period of 86400 seconds, the satellite altitude is 35855.91 km.
For a period of 86148 seconds, the satellite altitude is 35773.762 km.
For a period of 5400 seconds, the satellite altitude is 279.322 km.
For a period of 2700 seconds, the satellite altitude is -2181.56 km.

A satellite cannot orbit with a period of 45 minutes (2700 s). Therefore, a satellite's minimum altitude exists between a period of 90 minutes (5400 s) and 45 minutes. Technicaly, a geosynchoronous satellite is one that orbits the Earth once per sideral day, which is 23.93 hours (86148 s), not 24 hour (86400 s).

In [41]:
hours = array([24, 23.9344696])
length = hours*365
print(f"{round(length[0]-length[1],3)} hours difference in a year with solar days versus siderial days.")
23.919 hours difference in a year with solar days versus siderial days.

"Because Earth orbits the Sun once a year, the sidereal time at any given place and time will gain about four minutes against local civil time, every 24 hours, until, after a year has passed, one additional sidereal "day" has elapsed compared to the number of solar days that have gone by." Source.

Exercise 2.3: Converting cartesian coordinates

Write a program to perform the inverse operation to that of Example 2.2 for a 2-dimensional space.

In [43]:
x = 4.112
y = 6.05
r = sqrt(x**2 + y**2)
theta = arctan2(y,x)*(180/pi)
print(f"x = {x}, y = {y}")
print(f"r = {round(r,3)} units, theta = {round(theta,3)} degrees")
x = 4.112, y = 6.05
r = 7.315 units, theta = 55.797 degrees

Exercise 2.4: Relativistic spaceship

A spaceship travels from Earth in a straight line at relativistics speed v to another planet x light years away.

In [48]:
print("Choose x and v as a fraction of the speed of light c.")
x = 9.5
v = 0.95
c = 3e8 # m/s
t = x/v # dialated time (s)
t0 = t*sqrt(1-v**2) # proper time (s)
print(f"x = {x} light years, v = {v}c")
print(f"While {round(t0,2)} years pass on the space ship... {t} years pass on Earth!")
Choose x and v as a fraction of the speed of light c.
x = 9.5 light years, v = 0.95c
While 3.12 years pass on the space ship... 10.0 years pass on Earth!

Read about time dialation here.

Exercise 2.5: Quantum potential step

A well-known quantum mechanics problem involves a particle of mass m that encounters a one-dimensional potential step:
The particle with initial kinetic energy E and wavefactor $k_{1} = \sqrt{2mE}/\hbar$ enters from the left and encounters a sudden jump in potential energy of height V at position $x=0$. By solving the Schrödinger equation, one can show that when $E>V$ the particle may either (a) pass the step, in which case it has a lower kinetic energy of $E-V$ on the other side and a correspondingly smaller wavevector of $k_{2}=\sqrt{2m(E-V)}/\hbar$, or (b) it may be reflected, keeping all of its kinetic energy and unchanged wavevector but moving in the opposite direction. The probabilities T and R for transmission and reflection are given by:

$T = \frac{4k_{1}k_{2}}{(k_{1}+k_{2})^{2}}$ and $R = \left(\frac{k_{1}-k_{2}}{k_{1}+k_{2}}\right)^{2}$

Suppose we have a particle with mass equal to the electron mass m and energy 10 eV encountering a potential step of height 9 eV.

In [55]:
V = 9 # eV
E = 10 # eV
m = 9.11e-31 #kg
hbar = 6.5821e-16 # eV s
k1 = sqrt(2*m*E)/hbar
k2 = sqrt(2*m*(E-V))/hbar
T = (4*k1*k2)/(k1+k2)**2
R = ((k1-k2)/(k1+k2))**2
print(f"T = {round(T,3)}, R= {round(R,3)}")
print(f"T + R = {round(T+R,1)} because it represents the probability of all cases.")
T = 0.73, R= 0.27
T + R = 1.0 because it represents the probability of all cases.

Exercise 2.6: Planetary orbits

The orbit in space of one body around another, such as a planet around the Sun, need not be circular. In general it t akes the form of an ellipse, with the body sometimes closer in and sometimes further out. If yo uare given the distance $l_{1}$ of closest approach that a planet makes to the Sun, also called its perihelion, and its linear velocity $v_{1}$ at perihelion, then any other property of the orbit can be calculated from these two as follows.

  1. Kepler's second law tells us that the distance $l_{2}$ and velocity $v_{2}$ of the planet at its most distant point, or aphelion, satisfy $l_{2}v_{2}=l_{1}v_{1}$. At the same time the total energy, kinetic plus gravitational, of a planet with velocity v and distance r from the Sun is given by
    $E = \frac{1}{2}mv^{2}-G\frac{mM}{r}$
    where m is the planet's mass, M is the mass of the Sun, and G is Newton's graviational constant. Given that energy must be conserved, $v_{2}$ is the smaller root of the quadratic equation
    $v_{2}^{2}-\frac{2GM}{v_{1}l_{1}}v_{2}-\left[v_{1}^{2}-\frac{2GM}{l_{1}}\right]=0$.
    Once we have $v_{2}$ we can calculate $l_{2}$.
  2. Given the values of $v_{1}$, $l_{1}$, and $l_{2}$, other parameters of the orbit are given by simple formulas that can be derived from Kepler's laws and the fact that the orbit is an ellipse.
In [99]:
l1 = array([1.4710e11, 8.7830e10]) # m
v1 = array([3.0287e4, 5.4529e4]) # m/s
names = array(["Earth", "Halley's comet"])
G = 6.6738e-11 # m**3 k**-1 s**-2
M = 1.9891e30 # kg
c1 = (-2*G*M)/(v1*l1)
c2 = -(v1**2-2*G*M/l1)
roots = array([(-c1+sqrt(c1**2-4*c2))/2,(-c1-sqrt(c1**2-4*c2))/2])
v2 = array([min(roots[0]),min(roots[1])])
l2 = (l1*v1)/v2
a = 0.5*(l1+l2) # semi-major axis
b = sqrt(l1*l2) # semi-minor axis
T = (2*pi*a*b)/(l1*v1)
e = (l2-l1)/(l2+l1)
T = T/31536000

for i in range(len(names)):
    print(f"{names[i]}: orbital period = {round(T[i],2)} years, eccentricity = {e[i]}")
Earth: orbital period = 0.97 years, eccentricity = -6.223843261386815e-16
Halley's comet: orbital period = 76.08 years, eccentricity = 0.9672889126454061

Exercise 2.7: Catalan numbers

The Catalan numbers $C_{n}$ are a sequence of integers 1, 1, 2, 5, 14, 42, 132... that play an important role in quantum mechanics and the theory of disordered systems. They are given by

$C_{0} = 1$, $C_{n+1}=\frac{4n+2}{n+2}C_{n}$.

In [21]:
C = 1
n = 0
print(C)

while C <= 1e9:
    C = C*(4*n+2)/(n+2)
    print(int(C))
    n += 1
1
1
2
5
14
42
132
429
1430
4862
16796
58786
208012
742900
2674440
9694845
35357670
129644790
477638700
1767263190

Exercise 2.8: Order of operations

Suppose arrays a and b are defined. Compute:

  1. print(b/a+1)
  2. print(b/(a+1))
  3. print(1/a)
In [103]:
a = array([1,2,3,4],int)
b = array([2,4,6,8],int)

print(f"1. The result is {b/a+1}")
print(f"2. The result is {b/(a+1)}")
print(f"3. The result is {1/a}")
1. The result is [3. 3. 3. 3.]
2. The result is [1.         1.33333333 1.5        1.6       ]
3. The result is [1.         0.5        0.33333333 0.25      ]

Exercise 2.9: The Madelung constant

In condensed matter physics the Madelung constant gives the total electric potential felt by an atom in a solid. It depends on the chrages on the other atoms nearby and their locations. Consider for instance solid sodium chloride--table salt. The sodium chloride crystal has atoms arranged on a cubic lattice, but with alternating sodium and chlorine atoms, the sodium ones having a single positive charge +e and chlorine ones a single negative charge -e, wehter e is the charge on the electron. If we label each position on the lattice by three integer coordinates ( i , j ,k ), then the sodium atoms fall at positions where i + j + k is even, and the chlorine atoms at positions where i + j + k is odd.
Consider a sodium atom at the origin, i = j = k = 0, and let us calculate the Madelung constant. If the spacing of atoms on the lattice is a, then the distance from the origin to the atom at position ( i , j , k ) is

$\sqrt{(ia)^{2}+(ja)^{2}+(ka)^{2}}=a\sqrt{i^{2}+j^{2}+k^{2}}$,
and the potential at the origin created by such an atom is
$V(i,j,k) = \pm \frac{e}{4\pi \epsilon_{0}a\sqrt{i^{2}+j^{2}+k^{2}}}$
with $\epsilon_{0}$ being the permeittivity of the vacuum and the sign of the expression depending on whether _i + j + k_ is even or odd. The total potential felt by the sodium atom is then the sum of this quantity over all other atoms. Let us assume a cubic box around the sodium at the origin, with _L_ atoms in all direction. Then
$V_{total} = \sum_{i,j,k=-L\neq 0}^{L} V(i,j,k) = \frac{e}{4\pi \epsilon_{0} a}M$

where M is the Madelung constant, at least approximately (assuming large L).

In [26]:
L = 100 # number of atoms in all directions
a = 1 # spacing between atoms (m)
c = 1.112e-10 # C**2 J**-1 m**-1 (represents 4*pi*epsilon-naught)
e = 1.6022e-19 # C
pos = arange(a,L+a,a)
neg = arange(-L,0,a)

M = 0
for i in range(-L,L+1):
    for j in range(-L,L+1):
        for k in range(-L,L+1):
            
            if (i==j==k==0):
                continue
                
            M += (-1)**(i+j+k)/(sqrt(i**2+j**2+k**2))

print(f"The Madelung constant of NaCl is {abs(round(M,4))}")          
The Madelung constant of NaCl is 1.7418

Exercise 2.10: The semi-empirical mass formula

In nuclear physics, the semi-empirical mass formula is a formula for calculating the approximate nuclear binding energy B of an atomic nucleu with atomic number Z and mass number A:

$B = a_{1}A-a_{2}A^{2/3}-a_{3}\frac{Z^{2}}{A^{1/3}}-a_{4}\frac{(A-2Z)^{2}}{A}+\frac{a_{5}}{A^{1/2}}$
In [28]:
a1 = 15.8
a2 = 18.3
a3 = 0.714
a4 = 23.2

def a5calc(A,Z):
    if (A%2)!=0:
        return 0
    elif (Z%2)==0:
        return 12.0
    else:
        return -12.0

def Bcalc(A,Z,a5):
    return a1*A-a2*A**(2/3)-a3*(Z**2/A**(1/3))-a4*(A-2*Z)**2/A+a5/A**(1/2)

A = 58
Z = 28
a5 = a5calc(A,Z)
B = Bcalc(A,Z,a5)
N = B/A
print(f"The binding energy of an atom with A = {A} and Z = {Z} has total binding energy "
      f"B = {round(B,1)} MeV and binding energy per nucleon B/A = {round(N,2)} MeV")
The binding energy of an atom with A = 58 and Z = 28 has total binding energy B = 497.6 MeV and binding energy per nucleon B/A = 8.58 MeV
In [32]:
def stable(Z):
    A = arange(Z,3*Z+1,1)
    a5 = list()
    for a in A:
        a5.append(a5calc(a,Z))
    B = Bcalc(A,Z,a5)
    N = list(B/A)
    m = max(N)
    i = N.index(max(N))
    return (A[i],m)

Z = 28
print(f"The value of A that has the most stable nucleus for Z = {Z} is A = {stable(Z)[0]}")
Z = arange(1,101,1)
n = list()
a = list()
for z in Z:
    x = stable(z)
    a.append(x[0])
    n.append(x[1])
print(f"The maximum binding energy per nucleon is {round(max(n),3)} MeV and occurs at Z = {Z[n.index(max(n))]}"
      f" with A = {a[n.index(max(n))]}")
The value of A that has the most stable nucleus for Z = 28 is A = 62
The maximum binding energy per nucleon is 8.702 MeV and occurs at Z = 28 with A = 62

Exercise 2.11: Binomial coefficients

The binomial coefficient is an integer equal to:

$\binom{n}{k} = \frac{n!}{k!(n-k)!}$
where $k\geq 1$ or $\binom{n}{0}=1$ when $k=0$.

In [48]:
def factorial(n):
    f = 1.0
    for k in range(1,n+1):
        f *= k
    return f

def binomial(n,k):
    a = factorial(n)
    b = factorial(k)
    c = factorial(n-k)
    return a/(b*c)

def pascal(N):

    for n in range(0,N+1):
        c = n+1
        row = list()
        for k in range(c):
            row.append(int(binomial(n,k)))
        print(row)
        row.clear()
            
N = 12 # rows
print(f"The first {N} rows of Pascal's Triangle: \n")
pascal(N)
The first 12 rows of Pascal's Triangle: 

[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
[1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
[1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]

The probability that an unbiased coin, tossed n times, will come up heads k times is $\binom{n}{k}/2^{n}$.

In [52]:
def toss(n,k):
    return binomial(n,k)/2**n

n = 100
k = 60
print(f"The probability that a coin tossed {n} times comes up heads exactly {k} times is p = {round(toss(n,k),5)}")

q = 0
for m in range(0,k):
    q += toss(n,m)
p = 1-q
print(f"The probability that a coin tossed {n} times comes up heads {k} or more times is p = {round(p,5)}")
The probability that a coin tossed 100 times comes up heads exactly 60 times is p = 0.01084
The probability that a coin tossed 100 times comes up heads 60 or more times is p = 0.02844

Exercise 2.12: Prime numbers

We can develop a fast program for prime numbers by making use of the following observations:

  1. A number n is prime if it has no prime factors less than n. Hence we only need to check if it is divisible by other primes.
  2. If a number n is non-prime, having a factor r, then $n=rs$, where s is also a factor. If $r\geq\sqrt{n}$ then $ n=rs\geq \sqrt{ns}$, which implies that $s \leq \sqrt{n}$. In other words, any non-prime must have factors, and hence also prime factors, less than or equal to $\sqrt{n}$. Thus to determine if a number is prime we have to check its prime factors only up to and including $\sqrt{n}$ -- if there are none then the number is prime.
  3. If we find even a single prime factor less than $\sqrt{n}$ then we know that the number is non-prime, and hence there is no need to check any further -- we can abandon this number and move on to something else.
In [15]:
primes = [2]
N = 1000

for n in range(primes[0]+1,N+1):
    i=0
    boo = True
    while primes[i] <= sqrt(n):
        if (n%primes[i]) == 0:
            boo = False
            break
        i += 1
    if boo == True: primes.append(n)
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]

Exercise 2.13: Recursion

A useful feature of user-defined functions is recursion, the ability of a function to call itself. Recall the definiton of Catalan numbers in Exercise 2.7.

In [23]:
def catalan(n):
    if n == 0: 
        return 1
    else:
        return ((4*n+2)/(n+2))*catalan(n-1)
n = 100
print(f"the {n}th Catalan number is {catalan(n)}")
the 100th Catalan number is 3.533343320884637e+57

Euclid showed that the greatest common divisor $g(m,n)$ of two nonnegative integers m and n satisfies

$g(m,n) = m$ if $n=0$ and $g(m,n) = g(n,m\mod n)$ if $n>0$.
In [26]:
def gcd(m,n):
    if n == 0:
        return m
    else:
        return gcd(n, m%n)

m = 108
n = 192
print(f"The greatest common divisor of {m} and {n} is {gcd(m,n)}")
The greatest common divisor of 108 and 192 is 12