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):
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:
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