Reputation: 133
I'm writing a code in Python that predicts the energy levels of Hydrogen which I will use as a template for research into quarkonium energy levels. I'm using the scipy.integrate.odeint()
function to solve the Shroedinger equation and it works fine for the lower energy levels up to n=6
. I don't expect I'll have much need to go beyond that, but odeint returns Excess work done on this call (perhaps wrong Dfun type).
which only encourages me to extend what I can predict.
The Shroedinger equation substitution I'm using is:
u'' - (l*(l+1)/r**2 - 2mu_e(E-V_emag(r))) * u = 0
=>
u' = v
v' = ((l*(l+1))/(r**2) - 2.0*mu_e*(E - V_emag(r)))*u
I'm then using scipy.integrate.odeint()
on it and iterating through energies and using other functions I've defined that assess turning points and nodes in the result. The way I find the energy levels is finding the lowest possible E value where the number of turning points and nodes matches what it should; then incrementing L
by 1 and finding the new ground energy, e.g. if L=0
I'll find n=1
energy and if L=3
, I'll find the n=2
energy.
Once the code increments to L=7
it doesn't return anything useful. The range of r
has been extended but I've tried with keeping it the same to reduce the number of steps but to no avail. The code is self-taught so in my research I've read about Jacobians. I'm afraid I haven't worked out what they are or if I need one yet. Any ideas?
def v_emag(r):
v = -alpha/r
return v
def s_e(y,r,l,E): #Shroedinger equation for electromagntism
x = numpy.zeros_like(y)
x[0] = y[1]
x[1] = ((l*(l+1))/(r**2) - 2.0*mu_e*(E - V_emag(r)))*y[0]
return x
def i_s_e(l,E,start=0.001,stop=None,step=(0.005*bohr)):
if stop is None:
stop = ((l+1)*30-10)*bohr
r = numpy.arange(start,stop,step)
y = odeint(s_e,y0,r,args=(l,E))
return y
def inormalise_e(l,E,start=0.001,stop=None,step=(0.005*bohr)):
if stop is None:
stop = ((l+1)*30-10)*bohr
r = numpy.arange(start,stop,step)
f = i_s_e(l,E,start,stop,step)[:,0]
f2 = f**2
area = numpy.trapz(f2,x=r)
return f/(numpy.sqrt(area))
def inodes_e(l,E,start=0.001,stop=None,step=(0.005*bohr)):
if stop is None:
stop = ((l+1)*30-10)*bohr
x = i_s_e(l,E,start,stop,step)
r = numpy.arange(start,stop,step)
k=0
for i in range(len(r)-1):
if x[i,0]*x[i+1,0] < 0: #If u value times next u value <0,
k+=1 #crossing of u=0 has occured, therefore count node
return k
def iturns_e(l,E,start=0.001,stop=None,step=(0.005*bohr)):
if stop is None:
stop = ((l+1)*30-10)*bohr
x = i_s_e(l,E,start,stop,step)
r = numpy.arange(start,stop,step)
k = 0
for i in range(len(r)-1):
if x[i,1]*x[i+1,1] < 0: #If du/dr value times next du/dr value <0,
k=k+1 #crossing of du/dr=0, therefore a maximum/minimum
return k
l = 0
while l < 10: #The ground state for a system with a non-zero angular momentum will
E1 = -1.5e-08 #be the energy level of principle quantum number l-1, therefore
E3 = 0 #by changing l, we can find n by searching for the ground state
E2 = 0.5*(E1+E3)
i = 0
while i < 40:
N1 = inodes_e(l,E1)
N2 = inodes_e(l,E2)
N3 = inodes_e(l,E3)
T1 = iturns_e(l,E1)
T2 = iturns_e(l,E2)
T3 = iturns_e(l,E3)
if N1 != N2:# and T1 != T2: #Looks in lower half first, therefore will tend to ground state
E3 = E2
E2 = 0.5*(E1+E3)
elif N2 != N3:# and T2 != T3:
E1 = E2
E2 = 0.5*(E1+E3)
else:
print "Can't find satisfactory E in range"
break
i += 1
x = inormalise_e(l,E2)
if x[((l+1)**2)/0.005] > (x[2*((l+1)**2)/0.005]) and iturns_e(l,E2+1e-20)==1:
print 'Energy of state: n =',(l+1),'is: ',(E2*(10**9)),'eV'
l += 1
else:
E1 = E2+10e-20
Upvotes: 2
Views: 10987
Reputation: 25550
I don't know exactly what is wrong with your code and I'm not entirely sure what your while i<40:
loop is doing, so perhaps you can correct the following if I'm wrong.
If you want the wavefunctions for a certain n, l
for this system you can calculate the energy as E = RH/n^2 where RH is the Rydberg constant, so you don't need to count nodes. If you do need to count nodes, then the number corresponding to the (n,l)
is n-l-1
, so you can vary E and watch the number of nodes change for fixed l.
The main problem, it seems to me is that your r range isn't large enough to encompass all of the nodes (for large n ~ l), and that odeint
doens't know to stay away from the other (unphysical) asymptotic solution, psi ~ exp(+ cr), and so under some conditions sends psi off to ±infinity for large r.
If it's at all helpful, this is what I came up with to find numerical solutions to the SE equation: you need to vary the r
-range according to n,l
though to avoid the above problems (eg see what happens if you ask for n, l = 10, 9
).
import numpy as np
import scipy as sp
from scipy.integrate import odeint
m_e, m_p, hbar = sp.constants.m_e, sp.constants.m_p, sp.constants.hbar
mu_e = m_e*m_p/(m_e + m_p)
bohr = sp.constants.physical_constants['Bohr radius'][0]
Rinfhc = sp.constants.physical_constants['Rydberg constant times hc in J'][0]
RHhc = Rinfhc * mu_e / m_e
fac = sp.constants.e**2/4/sp.pi/sp.constants.epsilon_0
def V(r):
return -fac/r
def deriv(y, r, l, E):
y1, y2 = y
dy1dr = y2
dy2dr = -2*y2/r - (2*mu_e/hbar**2*(E - V(r)) - l*(l+1)/r**2)*y1
return dy1dr, dy2dr
def solveSE(l, E, y0):
rstep = 0.001 * bohr
rmin = rstep
rmax = 200*l * bohr #
r = np.arange(rmin, rmax, rstep)
y, dydt = odeint(deriv, y0, r, args=(l,E)).T
return r, y, dydt
n = 10
l = 2
y0 = (bohr, -bohr)
E = -RHhc / n**2
r, psi, dpsi_dr = solveSE(l, E, y0)
import pylab
pylab.plot(r, psi)
pylab.show()
Upvotes: 0