Quantenmechanik/ Magnetfeld/ Skript

Aus Wikibooks

Skript zum Zeeman-Effekt. Schreibt die Datei zeeman.svg

from math import sqrt

# computations for angular momentum

def colsalign(s) : # align string array s columns, strings start with ' '
  n=len(s); m=32; cols=[0]*m; kmax=0
  for i in range(n) :
    t=s[i]; h=0; j=t.find(' ',h+1); k=0
    while j>=0 : 
      if ((j-h)>cols[k]) : cols[k]=j-h-1
      h=j; j=t.find(' ',h+1); k+=1
    j=len(t) 
    if ((j-h)>cols[k]) : cols[k]=j-h-1
    if (k+1)>kmax : kmax=k+1
  # dbg print('Cols: '+str(cols[:kmax]))
  for i in range(n) :
    t=s[i]; h=0; j=t.find(' ',h+1); lt=len(t); k=0; v=''
    while j<=lt :
      u=t[h:j]+(' '*(cols[k]-(j-h-1))); v+=u  
      h=j; j= (lt+10) if (h==lt) else t.find(' ',h+1)
      k+=1; j= lt if (j<0) else j
    s[i]= v
  return s

def hasfact(x,z) : return (x>0) and ((x % z)==0)
def havefact(x,y,z) : # x,y have common factor z?
  fx=hasfact(x,z); fy=hasfact(y,z) 
  return (fx and fy) or (fx and (y==0)) or (fy and (x==0))

def normalize(x) : # rational-square root number x
  pn=[2,3,5,7,11,13,17,19,23]; ln= len(pn) # small prime nbrs
  ps=[0,1,4,9,16,25,36,49,64,81,100]; ls=len(ps) # small squares
  pr,pi,q,sp,sq = tuple(x)
  pr,sgr= (-pr,-1) if (pr<0) else (pr,1) 
  pi,sgi= (-pi,-1) if (pi<0) else (pi,1)
  for i in range(2,len(ps)) :
    z=ps[i]
    while hasfact(sp,z) : sp=div(sp,z); pr *= i; pi *= i
    while hasfact(sq,z) : sq=div(sq,z); q *=i
  for i in range(ln) :
    z=pn[i]
    while hasfact(sp,z) and hasfact(sq,z) : sp=div(sp,z); sq=div(sq,z)
    while havefact(pr,pi,z) and hasfact(q,z) :
      pr=div(pr,z); pi=div(pi,z); q=div(q,z)
  if (pr==0)and(pi==0) : q=1;sp=1;sq=1
  x[0],x[1],x[2],x[3],x[4] = (pr*sgr,pi*sgi,q,sp,sq)

def real(x) : # rational square root number, get real part
  pr,pi,q,sp,sq = tuple(x); return sqrt(sp/(sq+0.0))*pr/(q+0.0)

def encodenb(x) :
  pr,pi,q,sp,sq = tuple(x); s=''; w=''
  if (sp!=1)or(sq!=1) :
    w='\\sqrt{'; 
    w += str(sp) if(sq==1) else ('\\frac{'+str(sp)+'}{'+str(sq)+'}')
    w +='}'
  if pr != 0 :
    u=str(pr); s += u if (q==1) else ('\\frac{'+u+'}{'+str(q)+'}')
    s = w if((s=='1')and(w!='')) else (s if (s=='1') else (s+w))
  if pi != 0 : 
    if (pr!=0) and (pi>0) : s +='+i'
    elif (pi<0) : s += '-i'; pi=-pi
    else : s += 'i'
    u=str(pi); u='' if ((pi==1)and(q==1)) else u
    s += u if (q==1) else ('\\frac{'+u+'}{'+str(q)+'}')
    s = s+w
  if (pr==0)and(pi==0) : s='0' 
  return s

def displaynb(x) : # simple readable, root in square brackets
  pr,pi,q,sp,sq = tuple(x); s=''; w=''
  if (sp!=1)or(sq!=1) :
    w='['; 
    w += str(sp) if(sq==1) else (str(sp)+'/'+str(sq))
    w +=']'
  if pr != 0 :
    u=str(pr); s += u if (q==1) else (u+'/'+str(q))
    s = w if((s=='1')and(w!='')) else (s if (s=='1') else (s+w))
  if pi != 0 : 
    if (pr!=0) and (pi>0) : s +='+i'
    elif (pi<0) : s += '-i'; pi=-pi
    else : s += 'i'
    u=str(pi); u='' if ((pi==1)and(q==1)) else u
    s += u if (q==1) else (u+'/'+str(q))
    s = s+w
  if (pr==0)and(pi==0) : s='0' 
  return s

def reformat(x) : # int, rational, rcomplex to root numbers
  if type(x)==type(1) : z=[x,0,1,1,1]; n=-1
  elif type(x)==type([]) : n=len(x)
  if n==1 : z=[x[0],0,1,1,1]
  if n==2 : z=[x[0],0,x[1],1,1]
  if n==3 : z=[x[0],x[1],x[2],1,1]
  if n==5 : z=x
  if (n>5)or(n==0)or(n==4): print('reformat '+str(x)+' not supported.'); exit()
  return z

def multip(x,y) : # format (u,v,w,p,q) = (u+iv)/w sqrt(p/q)
  xx=reformat(x); yy=reformat(y)
  xpr,xpi,xq,xsp,xsq= tuple(xx); ypr,ypi,yq,ysp,ysq= tuple(yy)
  pr= xpr*ypr-xpi*ypi; pi= xpr*ypi+ypr*xpi; q=xq*yq; sp=xsp*ysp;sq=xsq*ysq
  z= [pr,pi,q,sp,sq]; normalize(z); return z

def addit(x,y) :
  xx=reformat(x); yy=reformat(y)
  return lincomb(xx,yy,1,1)

def sqroot(x) : # input should be (p,0,q,1,1) 
  pr,pi,q,sp,sq = tuple(x)
  if (pi!=0)or(sp!=1)or(sq!=1) : print('sqroot undefined.'); exit()
  sp=pr;sq=q;pr=1;q=1; z=[pr,pi,q,sp,sq]; normalize(z); return z

def imagi(x) : pr,pi,q,sp,sq = tuple(x); return [-pi,pr,q,sp,sq]
def scale(x,f) : pr,pi,q,sp,sq = tuple(x); return [pr*f,pi*f,q,sp,sq]

def lincomb(x,y,f,g) : # f,g integer
  # a sqrt(r/s) + b sqrt(p/q) ?= sqrt(rp/qs) ?
  # can only be combined if r/s and p/q differ by square factor T/U ?
  # first write rq/qs, ps/qs and test if rq,ps have square factor
  ps=[0,1,4,9,16,25,36,49,64,81,100]; ls=len(ps) # small squares
  xpr,xpi,xq,xsp,xsq= tuple(x); xpr *= f; xpi *=f
  ypr,ypi,yq,ysp,ysq= tuple(y); ypr *= g; ypi *=g
  if (xpr==0)and(xpi==0) :   z=[ypr,ypi,yq,ysp,ysq]
  elif (ypr==0)and(ypi==0) : z= [xpr,xpi,xq,xsp,xsq] 
  else :
    sq=xsq*ysq; xp=xsp*ysq; yp=ysp*xsq
    for i in range(2,len(ps)) :
      z=ps[i]
      while hasfact(xp,z) : xp=div(xp,z); xpr *= i; xpi *= i
      while hasfact(yp,z) : yp=div(yp,z); ypr *= i; ypi *= i 
    # here xp/sq new sqrt arg? if yp=n xp, rewrite x ?
    if xp != yp : print('lincomb:sqrt clash'+str([xp,yp])); exit()
    sp=xp; q=xq*yq; pr=xpr*yq+ypr*xq; pi=xpi*yq+ypi*xq
    z= [pr,pi,q,sp,sq]
  normalize(z); return z

def matshow(s,x,new=0) :
  # new= 1 simplified, columns. new=0 raw.
  # new= 2 use encodenb, latex-like, check square hermitian 
  n=len(x); m=len(x[0]); print(s); t=''; ok=True; tt=['']*n
  for i in range(n) :
    for k in range(m) :
      if (new==2) and (k>0) : t +=' &'
      if (new==1) : t +=' '+displaynb(x[i][k])
      if (new==2) : t +=' '+encodenb(x[i][k])
      if new==0 : t +='  '+str(x[i][k]) # beware about v, we need a copy!
      if new==2 :
        u=x[i][k]; v= [] + x[k][i]; v[1]= -v[1]; ok= ok and isequal(u,v)
    if (new==2) and (i<(n-1)) : t +=' \\\\';
    tt[i]=t; t=''
  if new==1 : colsalign(tt)
  for i in range(n) : print(tt[i])
  if new==2 : print('Test Hermitian: '+str(ok))
  return tt

def vecshow(s,x) :
  n=len(x); print(s); t=''
  for k in range(n) : t +=' '+displaynb(x[k])
  print(t)

def paulimats() :
  z=[0,0,1,1,1]; u=[1,0,1,1,1]; i=[0,1,1,1,1]; j=[0,-1,1,1,1]; v=[-1,0,1,1,1]
  sig=[[],[],[],[]]
  sig[0]= [[u,z],[z,u]]
  sig[1]= [[z,u],[u,z]] ; matshow('S1',sig[1])
  sig[2]= [[z,j],[i,z]] ; matshow('S2',sig[2])
  sig[3]= [[u,z],[z,v]] ; matshow('S3',sig[3])
  for h in range(1,4) :
    for k in range(1,4) :
      a= matmul(sig[h],sig[k]); matshow('S'+str(h)+'*S'+str(k),a)

def div(p,q) :
  if p>=0 : return int(p/q)
  else : return -int(-p/q)

def getprimes(n) : # want n prime numbers
  p=[2]; i=1; plast=2
  while i<=n :
    q=plast+1; found=False
    while not found:
      j=0
      while ((q%p[j])!=0) and ((p[j]*p[j])<=q) :  j +=1
      if (q%p[j] != 0) : p += [q]; plast=q; found=True; i+=1
      else : q+=1
  return p

def greatestdiv(x) : # greatest divisor, skip 0s in list
  n=len(x); y=[0]*n; w=[0]*n; h=0; ymin=-1; gcd=1
  pn=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]; ln= len(pn) # small primes
  for i in range(n) :
    z=abs(x[i])
    if z>0 : ymin= z if(ymin<0) else min(ymin,z); w[h]=z; h+=1
  w=w[:h]; ip=0
  while (ip<ln)and(pn[ip]<=ymin) :
    z=pn[ip]; ok=True
    while ok :
      for i in range(h) : ok= ok and ((w[i]%z)==0)
      if ok : 
        for i in range(h) : w[i]= div(w[i],z)
        ymin= div(ymin,z); gcd *= z
    ip +=1 
  return gcd    

def smallestmult(x) : # smallest multiple of nonzero numbers
  n=len(x); y=abs(x[0])
  for i in range(1,n) : 
    z=abs(x[i]); y = y if (z==0) else div(y*z,greatestdiv([y,z]))
  return y

def divide(p,q) : # improve integer fractions
  def hasfact(x,z) : return (x>0) and ((x % z)==0)
  pn=[2,3,5,7,11,13,17,19,23,29,31,37]; ln= len(pn) # small prime nbrs
  p,q,s = (abs(p),abs(q),-1) if ((p*q)<0) else (abs(p),abs(q),1) 
  if p==0 : q=1
  for i in range(ln) :
    z=pn[i]
    while hasfact(p,z) and hasfact(q,z) : p=div(p,z); q=div(q,z)
  return [s*p,q]

'''
Basic fine-struct & zeeman effect model.
  Old basis: L3 and S3 diag. New: 2L*S=[j(j+1)-l(l+1)-s(s+1)] diag.
  S3 does not commute with J^2, but with J3,L3,S3,S^2,L^2.
  matrix elements of L*S, L3 and S3 in new basis Ci, from old basis Bk:
    (Ci,Op,Cj)= (sum k Mik, sum h Mjh) (Bk,Op,Bh) with matrix transform M
  matrices l3,s3 have same nonzero patterns
  scalar products <j,j3(L3,S3)j',j3'> wont stay in j-block, but j3-blocks.
'''

def fullmatrix(diag,mat) : # diag + x * mat, latex-style
  t='\\begin{pmatrix}{llllll}\n'; n=len(diag)
  for i in range(n) :
    for k in range(n) :
      mik=mat[i][k]; zero= (mik[0]==0); dg=diag[i]
      v=encodenb(mik); neg=(v[0]=='-'); zero=(v[0]=='0')
      w= ('-x\cdot'+v[1:]) if neg else ('0' if zero else ('x\cdot'+v))
      if i==k : u=encodenb(dg); u+= w if neg else ('+'+w)
      else : u=w
      if k>0 : t += ' & '
      t+= u
    if (i<(n-1)) : t +=' \\\\';
    t+='\n'
  t+='\\end{pmatrix}\n'
  print(t)

def jj3(j,j3, l,l3, s,s3) : # get coeffs of vector (j,j3). numbers halfints
  j2=div(j[0]*2,j[1]); j32=div(j3[0]*2,j3[1]) 
  l2=div(l[0]*2,l[1]); l32=div(l3[0]*2,l3[1]) 
  s2=div(s[0]*2,s[1]); s32=div(s3[0]*2,s3[1]) 
  ok=True; jmax=abs(l2)+abs(s2); jmin=abs(l2-s2); z=[0,1]; sign=0
  if (s2 !=1) : print('spin s '+str(s)+' not supported'); ok=False
  if (j2>jmax)or(j2<jmin) : print('sum j '+str(j)+' out of range'); ok=False
  if (j32>j2)or(j32<-j2)or(l32>l2)or(l32<-l2)or(s32>s2)or(s32<-s2) :
    print('one of '+str([j3,l3,s3])+' out of range'); ok=False
  if (j2>l2) : # contribute l3,s3= (j3-0.5,+) and (j3+0.5,-)
    if (l32==(j32-1))and(s32==1) : z=[l2+j32+1,2*l2+2]; sign=1
    if (l32==(j32+1))and(s32==-1) : z=[l2-j32+1,2*l2+2]; sign=1
  else  : # contribute l3,s3= (j3+0.5,-) and (j3-0.5,+)
    if (l32==(j32+1))and(s32==-1) : z=[l2+j32+1,2*l2+2]; sign=1
    if (l32==(j32-1))and(s32==1) : z=[l2-j32+1,2*l2+2]; sign=-1
  if (not ok)or(z[0]==0) : z= [0,0,1,1,1]
  else : z=[sign,0,1,z[0],z[1]]
  return z

def testcleb(j,l) : # Clebsch-Gordan case spin 1/2 only
  # make a matrix of coeffs for (j,j3) in (l,l3)(x)(s,s3)
  noisy=False # example l=[1,1]; j=[3,2]; s fixed for spin 1/2
  nl= div(2*l[0],l[1])+1
  s=[1,2]; ns= div(2*s[0],s[1])+1
  nj= div(2*j[0],j[1])+1; mx=[[]]*nj 
  for ij in range(nj) :
    j3=j if (ij==0) else [j3[0]-j3[1],j3[1]]; hj=0; mx[ij]=[[]]*(nl*ns) 
    if ij==0 : s3val=[[]]*(nl*ns); l3val=[[]]*(nl*ns)
    for il in range(nl) :
      l3=l if (il==0) else [l3[0]-l3[1],l3[1]] 
      for ks in range(ns) :
        s3=s if (ks==0) else [s3[0]-s3[1],s3[1]] 
        if ij==0 : s3val[hj]=s3; l3val[hj]=l3
        z= jj3(j,j3, l,l3,s,s3); normalize(z); mx[ij][hj]=z; hj+=1
        if noisy : print(str([ij,il,ks,z]))
  # matshow('',mx,1); return mx,s3val,l3val
  return mx,s3val,l3val

def matpermute(m,s) : # matrix line-column permutation
  n=len(m); mp=[[]]*n; p=[0]*n
  for i in range(n) : p[i]=int(s[i]); mp[i]=[[]]*n
  for i in range(n) :
    for k in range(n) : mp[i][k]=m[p[i]][p[k]]
  return mp

def vecpermute(v,s) : # vector permutation
  n=len(v); vp=[[]]*n
  for i in range(n) : p=int(s[i]); vp[i]= v[p]
  return vp

def spectralcurve(dg,m) : # vary Dg+xM and compute 6 values for each
  # dg diagonal matrix, M 2x2-block matrix
  # wrong, should get 3 not 5 asymptotics ? 2 triplets with diff g-factors?
  nx=100; fx=5.0/nx; curve=[[]]*6; y=[0]*6
  for j in range(6) : curve[j]=[0.0]*(2*nx+2)
  for ix in range(nx+1) :
    x=fx*ix; y[0]=real(dg[0])+real(m[0][0])*x
    y[1]=real(dg[1])+real(m[1][1])*x # 1st block trivial
    for j in range(2) :
      k=2+2*j; a=real(dg[k])+real(m[k][k])*x;
      b=real(m[k][k+1])*x; c=real(m[k+1][k])*x
      d=real(dg[k+1])+real(m[k+1][k+1])*x
      # solve (a-z)(d-z)=bc=zz-(a+d)z+ad; (z-p)^2=...
      q=b*c-a*d; p=0.5*(a+d); root=sqrt(q+p*p) 
      y[k]=p+root; y[k+1]=p-root
    for j in range(6) : curve[j][2*ix]=x; curve[j][2*ix+1]=y[j]
  return curve      

def zeeman() :  
  # use special multip and addit for square-root numbers
  l=[1,1]; s=[1,2]; ja=[3,2]; jb=[1,2] # combine ang-momenta l,s to ja,jb
  mx4,s3val,l3val= testcleb(ja,l)
  mx2,s3val,l3val= testcleb(jb,l)
  mx= mx4+mx2; n=len(mx); diag=[[]]*n; noisy=False # mx is 6x6 matrix
  print('Dimension of mx='+str(len(mx))+','+str(len(mx[0])))
  # diag vector has (LS) = [j(j+1)-l(l+1)-s(s+1)]/2 entries
  z=addit(ja,1); ua=multip(ja,z) 
  z=addit(jb,1); ub=multip(jb,z) 
  z=addit(l,1); v=multip(l,z) 
  z=addit(s,1); w=multip(s,z) 
  x=lincomb(ua,v,1,-1); x=lincomb(x,w,1,-1); ua=multip(x,[1,2])
  x=lincomb(ub,v,1,-1); x=lincomb(x,w,1,-1); ub=multip(x,[1,2])
  for i in range(len(mx4)) : diag[i]= ua
  for i in range(len(mx4),n) : diag[i]= ub
  if noisy : print('ua,ub='+str(ua)+str(ub))
  if noisy : matshow('Matrix mx',mx,1)
  s3=[[]]*n; l3=[[]]*n; l2s=[[]]*n
  for i in range(n) :
    s3[i]=[[]]*n; l3[i]=[[]]*n; l2s[i]=[[]]*n
    for j in range(n) : # compute op[i][j]
      s3sum=0; l3sum=0
      for k in range(n) :
        u= s3val[k]; v=l3val[k] # the diagonal op(k,h)
        u1= multip(u,mx[i][k]); v1=multip(v,mx[i][k])
        for h in (k,) : # not 'in range(n)' because diagonal
          u2=multip(u1,mx[j][h]); s3sum= addit(s3sum,u2)
          v2=multip(v1,mx[j][h]); l3sum= addit(l3sum,v2)
      s3[i][j]=s3sum; l3[i][j]=l3sum 
      l2s[i][j]=lincomb(l3sum,s3sum,1,2) # L+2S matrix
  if noisy : matshow('Matrix s3',s3,1); matshow('Matrix l3',l3,1)
  if noisy : matshow('Matrix l2s',l2s,1)
  l2sp= matpermute(l2s,'031425'); diagp= vecpermute(diag,'031425')
  matshow('Matrix l2s permuted',l2sp,1)
  vecshow('Vector diag permuted', diagp)
  data= spectralcurve(diagp, l2sp); multiplot(data) # SVG plot
  print('\nPlot zeeman.svg written, for Perturbation Matrix\n')
  fullmatrix(diagp, l2sp) # Latex output

# svg sketch 6 levels vs fieldstrength

def multiplot(d) :
  n=len(d); xmin=1e12;xmax=-xmin;ymin=xmin;ymax=xmax
  txt=[]; fn='zeeman'; ix=[0,1,2,3,4,5,6,7,8,9]
  for i in range(n) :
    k=0; kmax=len(d[i]); #print('Curve '+str(i)+' length '+str(kmax))
    while k<kmax :
      x=d[i][k]; y=d[i][k+1]; k+=2 
      xmin= x if(x<xmin) else xmin; xmax= x if (x>xmax) else xmax
      ymin= y if(y<ymin) else ymin; ymax= y if (y>ymax) else ymax
  #print('xmin,ymin, xmax,ymax'+str([xmin,ymin, xmax,ymax])) 
  xyf=[ xmin,ymin, xmax,ymin, xmax,ymax, xmin,ymax, xmin,ymin]
  xys=[xyf]+d
  xs,fx,xpix= xmin, 750/(xmax-xmin),25 # scale user units, min in pixels
  ys,fy,ypix= ymin,-550/(ymax-ymin),575 ; scale=(xs,fx,xpix, ys,fy,ypix)
  colo=['#aaa','#00a','#077','#0a0','#770','#a00','#707','#0ff','#f0f']
  sd=svgdump(); buf=sd.plotbegin(800,600,1); i=0; dy=(ymax-ymin)/20.0
  for j in range(len(txt)) :
    buf += label(sd,scale, txt[j], 0.2*xmin+0.8*xmax,ymax-dy-j*dy)
  for t in xys : buf += curve(sd,scale, t, fill=colo[ix[i]]); i+=1
  g=open(fn+'.svg','w'); g.write(buf+sd.plotend()); g.close()

class svgdump :
  def plotbegin(sd, w,h,f) : # args: width height zoomfactor
    W=str(w); H=str(h); u=''; fw=str(f*w); fh=str(f*h)
    s=( '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'+
     '<!DOCTYPE svg>\n'+
     '<svg xmlns="http://www.w3.org/2000/svg"\n'+
     '  xmlns:xlink="http://www.w3.org/1999/xlink"\n'+
     '  width="'+W+'" height="'+H+'" viewBox="0 0 '+fw+' '+fh+'">\n')
    return s

  def plotend(sd) : return '</svg>\n'
  def move(sd,x,y) : return 'M'+str(x)+','+str(y)
  def line(sd,x,y) : return 'L'+str(x)+','+str(y)
      
  def labl(sd,s,x,y, size=14,fill='#000') :
    f=' font-family="Arial"'
    t='<tspan x="'+str(x)+'" y="'+str(y)+'">'+s+'</tspan>'; fsz= str(size)
    return ('<text fill="'+fill+'"'+f+' font-size="'+fsz+'px">'+t+'</text>\n')

  def path(sd,w,s,lc='#000') : # path w=with lc=linecolor
    t='<path fill="none" stroke="'+lc+'" stroke-width="'+str(w)+'" '
    t+= ' stroke-linecap="round" ' 
    return t + 'd="\n'+s+'" />\n'

# end class svgdump

def aprx(x) : # 3-digit approximation
  y= (-x) if(x<0) else x; z= int(1e3*y+1e-3); z= (-z) if (x<0) else z
  return z/1000.0

def curve(sd,scale,xy, fill='#000', width=1) :
  (xs,fx,xmin, ys,fy,ymin) = scale; s=''; n= div(len(xy),2) 
  for i in range(n) : 
    x= aprx(xmin+fx*(xy[2*i]-xs)); y= aprx(ymin+fy*(xy[2*i+1]-ys))
    s+= sd.move(x,y) if(i==0) else sd.line(x,y)
  return sd.path(width,s,lc=fill)

def label(sd,scale,txt,tx,ty) : # tx,ty plot coordinates not pixels
  (xs,fx,xmin, ys,fy,ymin) = scale 
  x= aprx(xmin+fx*(tx-xs)); y= aprx(ymin+fy*(ty-ys))
  return sd.labl(txt,x,y)

zeeman()