abcd_optics.py

Go to the documentation of this file.
00001 """Gaussian (paraxial) Optics matrix formalism and diffration calculations
00002 Marcus H. Mendenhall, Vanderbilt University Keck Free Electron Laser Center, Nashville, TN, 37235
00003 16 April, 2002
00004 This is really the little brother of general_optics.py, which mostly should be used instead"""
00005 _rcsid="$Id: abcd_optics.py,v 1.5 2007/11/08 22:45:27 mendenhall Exp $"
00006 
00007 import math
00008 
00009 try:
00010   import numpy as Numeric
00011   import numpy.linalg
00012   numeric_float=Numeric.float
00013   numeric_complex=Numeric.complex
00014   eigenvectors=numpy.linalg.eig
00015 
00016 except:
00017   import Numeric
00018   import LinearAlgebra
00019   numeric_float=Numeric.Float
00020   numeric_complex=Numeric.Complex
00021   eigenvectors=LinearAlgebra.eigenvectors
00022 
00023 Infinity="Infinity"
00024 
00025 class qparms:
00026   "qparms is a class for the q-parameter structure"
00027   def __init__(self, lambda0, q=None, w=None, r=None):
00028     if q is None:
00029       if r is Infinity:
00030         rinv=0
00031       else:
00032         rinv=1.0/r
00033       self.q=1.0/ complex(rinv , -lambda0 / (math.pi*w**2) )
00034       self.w=w
00035       self.r=r
00036     else:
00037       self.q=q
00038       qi=1.0/q
00039       self.w=math.sqrt(-lambda0/qi.imag/math.pi)
00040       if abs(qi.real) < 1e-15:
00041         self.r=Infinity
00042       else: 
00043         self.r=1.0/qi.real      
00044     self.lambda0=lambda0
00045   
00046   def __str__(self):
00047     if self.r is Infinity:
00048       rstr=self.r
00049     else:
00050       rstr="%.3f"%self.r
00051     return "lambda= %.4e, q= (%.3f %+.3fj), w=%.3f,  r=%s"%(self.lambda0, self.q.real, self.q.imag, self.w, rstr)
00052     
00053   def qw(self, abcd):
00054     if isinstance(abcd, optic):
00055       a,b,c,d=abcd.abcd().flat
00056     else:
00057       a,b,c,d=abcd.flat
00058     qout=qparms(self.lambda0, q=(self.q*a+b)/(self.q*c+d))
00059     return qout
00060     
00061   def ztrace(self, matlist, end_z):
00062     return ztrace(matlist, end_z, self)
00063 
00064 def fixname(name):
00065   if name is not None:
00066     return "("+name+")"
00067   else:
00068     return ""
00069 
00070 class optic:
00071   def __init__(self, abcd,  tagstr=None):
00072     if tagstr is not None:
00073       self.tag=tagstr
00074     else:
00075       self.tag=""
00076     self.mat=abcd
00077     self.qparm=None
00078     
00079   def __str__(self):
00080     return self.tag
00081   def abcd(self):
00082     return self.mat
00083   
00084   def setqparm(self, qparm=None):
00085     self.qparm=qparm
00086     
00087   def __mul__(self, other):
00088     "optic2*optic1 is an optic representing the right-hand optic followed by the left i.e. left matrix multiplication semantics"
00089     if isinstance(other, optic):
00090       mat=Numeric.dot(self.mat, other.mat)
00091       return optic(mat , "Composite Optic:  \n"+Numeric.array_str(mat, precision=3, suppress_small=1))
00092     else:
00093       return Numeric.dot(self.mat, other)
00094 
00095   def __add__(self, other): #addition runs left-to-right 
00096     "optic1+optic2 is an optic representing the left-hand optic followed by the right i.e. algebraic semantics"
00097     if isinstance(other, optic):
00098       mat=Numeric.dot(other.mat, self.mat)
00099       return optic(mat, "Composite Optic:  \n"+Numeric.array_str(mat, precision=3, suppress_small=1) )
00100     else:
00101       return Numeric.dot(other, self.mat)
00102       
00103 class space(optic):
00104   "space(l) returns abcd for a drift space of length l"
00105   def __init__(self, l, name=None):
00106     optic.__init__(self, Numeric.array(((1.0,l),(0.0,1.0))), 
00107         "Drift space%s:, l=%.3f" % (fixname(name), l) )
00108 
00109 class lens(optic):
00110   "lens(f) returns abcd for a lens of focal length f"
00111   def __init__(self, f, name=None):
00112     optic.__init__ (self, Numeric.array(((1.0,0.0),(-1.0/f,1.0))),  
00113         "Lens%s: f= %.3f"%(fixname(name), f))
00114 
00115 class identityoptic(optic):
00116   "identityoptic is a class used to tag optics which do not actually change the beam"
00117   def __init__(self, name=None):
00118     optic.__init__ (self, ((1,0),(0,1)),  name)
00119   
00120 class aperture(identityoptic):
00121   "aperture(diameter) returns an identity optic labelled by diameter"
00122   def __init__(self, dia, name=None):
00123     identityoptic.__init__ (self, 
00124         "Aperture%s: dia= %.3f"%(fixname(name),dia))
00125     self.diameter=dia
00126     
00127   def __str__(self):
00128     if self.qparm is None:
00129       return self.tag
00130     else:
00131       transmission=1.0-math.exp(-((self.diameter/(2*self.qparm.w))**2))
00132       return self.tag + (" Transmission = %.3f" % transmission)
00133     
00134 class surface(optic):
00135   "surface(r, n) returns abcd for a dielectric surface with radius-of-curvature r and index ratio n"
00136   def __init__(self, r, n, name=None):
00137     optic.__init__(self, Numeric.array(((1.0,0.0),((1.0-n)/(n*r),1.0/n))),  
00138         "Dielectric surface%s: n'=%.3f r=%.3f" % (fixname(name), n,r) )
00139 
00140 class mirror(optic):
00141   "mirror(r) returns abcd for a mirror with radius-of-curvature r (f=r/2)"
00142   def __init__(self, r, name=None):
00143     optic.__init__(self, Numeric.array(((1.0,0.0),(-2.0/r,1.0))), 
00144         "Mirror%s: roc=%.3f" % (fixname(name), r) )
00145 
00146 class probe(identityoptic):
00147   "probe() returns an identity optic but is handled in matx and ztrace as adding no length"
00148   def __init__(self, name=None):
00149     identityoptic.__init__(self, 
00150         "Probe%s" % (fixname(name), ) )
00151       
00152 def resonator(r1, r2, l, lambda0):
00153   "resonator(r1, r2, length, lambda0) returns (z0, w0, w(mirror1), w(mirror2), offset for resonator with mirrors and length"
00154   abcd0=mirror(r1)*space(l)* mirror(r2)* space(l)
00155   art,brt,crt,drt=tuple(abcd0.abcd().flat)
00156   iqci2= (1.0 - (art+drt)**2 / 4) / brt**2
00157   rqci = (drt - art) / (2.0*brt)
00158   zoff = -rqci / (rqci**2 + iqci2)
00159   z0 = math.sqrt(iqci2) / (rqci**2 + iqci2)
00160   w0 = math.sqrt((lambda0*z0)/math.pi)
00161   return z0, w0, w0*math.sqrt(1+(zoff/z0)**2), w0*math.sqrt(1+((l-zoff)/z0)**2), zoff-l/2
00162 
00163 def abcd_resonator(abcd_rt, lambda0):
00164   "resonator(abcd_rt,  length, lambda0) returns (q0, w0, r0) for resonator with round-trip abcd_rt at wavelength lambda"
00165   abcd0=abcd_rt 
00166   art,brt,crt,drt=tuple(abcd0.abcd().flat)
00167   iqci2= (1.0 - (art+drt)**2 / 4) / brt**2
00168   rqci = (drt - art) / (2.0*brt)
00169   qout = 1.0 / complex(rqci,  -math.sqrt(iqci2) )
00170   return qparms(lambda0, q=qout)
00171 
00172 def matx(matlist, x):
00173   "matx( ((drift0, abcd0),(drift1, abcd1)...), x) returns the accumulated abcd matrix at x.  Drift_n is _before_ the element abcd_n"
00174   m=optic(Numeric.identity(2).astype(numeric_float))
00175   z=0.0
00176   for (dz, abcd) in matlist:
00177     if not isinstance(abcd, probe):
00178       if z+dz >= x: break
00179       z += dz
00180       m=abcd*space(dz)*m
00181   return space(x-z)*m
00182   
00183 class ztrace:
00184   """ztrace(matlist, end_dist, q0) creates a list of information about the beam through the system"""
00185   def __init__(self, matlist, end_dist, q0):
00186     zpos=0.0
00187     objectpos=[]
00188     objects=[]
00189     for (dz,obj) in matlist: #mark out positions of optical components, backup up over probes
00190       objectpos.append(zpos+dz)
00191       if not isinstance(obj,probe): zpos+=dz
00192       objects.append(obj)
00193     zpos=0.0
00194     opticindex=0
00195     resultlist=[]
00196     doing_optic=0
00197     while (zpos < end_dist):
00198       qparm=q0.qw(matx(matlist, zpos))
00199       next_waist=zpos-qparm.q.real
00200       resultlist.append((zpos, qparm))
00201       if opticindex < len(objectpos):
00202         next_optic=objectpos[opticindex]
00203         optic=objects[opticindex]
00204       else:
00205         next_optic=end_dist+1
00206         optic=None
00207       if not doing_optic:
00208         if next_waist > zpos and next_waist < next_optic-1:
00209           zpos=next_waist
00210         else:
00211           if isinstance(optic, identityoptic):
00212             zpos=next_optic
00213             optic.setqparm(q0.qw(matx(matlist, zpos)))
00214             resultlist.append((objectpos[opticindex],str(optic)))
00215             opticindex += 1
00216           else:
00217             zpos=next_optic-1
00218             doing_optic=1
00219       else:
00220         resultlist.append((objectpos[opticindex],str(matlist[opticindex][1])))
00221         zpos+=2
00222         opticindex+=1
00223         doing_optic=0
00224     resultlist.append((end_dist, q0.qw(matx(matlist, end_dist))))
00225     self.trace=resultlist
00226     
00227   def __str__(self):
00228     thestr="\n\n***Starting beam trace***\n"
00229     for (z,info) in self.trace:
00230       thestr += ("%10.1f  " % z)
00231       if type(info)==type(" "):
00232         thestr+= info 
00233       else:
00234         w, r = info.w, info.r
00235         if r is Infinity:
00236           rstr="r="+r
00237         elif abs(r)>10000.0:
00238           rstr=("r=%10.2e" %r)
00239         else:
00240           rstr="r=%10.1f" % r
00241         thestr += ("w=%6.2f "%w) + rstr
00242       thestr += "\n"
00243     return thestr

Generated on Wed Nov 21 10:18:31 2007 for analysis by  doxygen 1.5.4