1 '''Defines the GaussianBeam class for theia.'''
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import numpy as np
20 from ..helpers import geometry
21 from ..helpers.tools import formatter
22 from ..helpers.units import pi, nm, deg, mm
23
25 '''
26
27 GaussianBeam class.
28
29 This class represents general astigmatic Gaussian beams in 3D space.
30 These are the objects that are intended to interact with the optical
31 components during the ray tracing and that are rendered in 3D thanks to
32 FreeCAD.
33
34 *=== Attributes ===*
35 BeamCount: class attribute, counts beams. [integer]
36 Name: class attribute. [string]
37 QTens: general astigmatic complex curvature tensor at the origin.
38 [np. array of complex]
39 N: Refraction index of the medium in which the beam is placed. [float]
40 Wl: Wave-length in vacuum of the beam (frequency never changes). [float]
41 P: Power of the beam. [float]
42 Pos: Position in 3D space of the origin of the beam. [3D vector]
43 Dir: Normalized direction in 3D space of the beam axis. [3D vector]
44 U: A tuple of unitary vectors which along with Dir form a direct orthonormal
45 basis in which the Q tensor is expressed. [tuple of 3D vectors]
46 Ref: Reference to the beam. [string]
47 OptDist: Optical length. [float]
48 Length: Geometrical length of the beam. [float]
49 StrayOrder: Number representing the *strayness* of the beam. If the beams
50 results from a transmission on a HR surface or a reflection on a AR
51 surface, then its StrayOrder is the StrayOrder of the parent beam + 1.
52 [integer]
53 Optic: Ref of optic where the beam departs from (None if laser). [string]
54 Face: face of the optic where the beam departs from. [string]
55
56 '''
57 BeamCount = 0
58 Name = "Beam"
59
60 - def __init__(self, Q, N, Wl, P, Pos, Dir, Ux, Uy, Ref, OptDist,
61 Length, StrayOrder, Optic, Face):
62 '''Beam initializer.
63
64 This is the initializer used internally for beam creation, for user
65 inputed beams, see class method userGaussianBeam.
66
67 Returns a Gaussian beam with attributes as the parameters.
68
69 '''
70
71
72 self.N = N
73 self.Wl = Wl
74 self.P = P
75 self.OptDist = OptDist
76 self.Length = Length
77 self.StrayOrder = StrayOrder
78
79 self.Ref = Ref
80
81 self.Pos = Pos
82 self.Dir = Dir
83
84
85 self.U = (Ux, Uy)
86
87
88 self.QTens = np.array(Q, dtype = np.complex64)
89
90
91 self.Optic = Optic
92 self.Face = Face
93
94 self.__class__.BeamCount = self.__class__.BeamCount + 1
95
97 '''String representation of the beam, when calling print(beam).
98
99 '''
100 return formatter(self.lines())
101
103 '''Returns the list of lines necessary to print the object.
104
105 '''
106 ans = []
107 ans.append("Beam: %s {" %self.Ref )
108 ans.append("Power: %sW/Index: %s/Wavelength: %snm/Length: %sm" \
109 %(str(self.P), str(self.N), str(self.Wl/nm), str(self.Length)))
110 ans.append("Order: %s" %str(self.StrayOrder))
111 ans.append("Origin: %s" %str(self.Pos))
112
113 sph = geometry.rectToSph(self.Dir)
114 ans.append("Direction: (%s, %s)deg" %(str(sph[0]/deg), str(sph[1]/deg)))
115
116 sphx = geometry.rectToSph(self.U[0])
117 ans.append("Ux: (%s, %s)deg" %(str(sphx[0]/deg), str(sphx[1]/deg) ))
118
119 sphy = geometry.rectToSph(self.U[1])
120 ans.append("Uy: (%s, %s)deg" %(str(sphy[0]/deg), str(sphy[1]/deg)))
121 ans.append("Waist Pos: %sm" % str(self.waistPos()) )
122 ans.append("Waist Size: (%s, %s)mm" \
123 %(str(self.waistSize()[0]/mm), str(self.waistSize()[1]/mm)))
124 ans.append("Rayleigh: %sm" % str(self.rayleigh()) )
125 ans.append("ROC: " + str(self.ROC()))
126 ans.append("}")
127
128 return ans
129
130 - def Q(self, d = 0.):
131 '''Return the Q tensor at a distance d of origin.
132
133 '''
134 d = float(d)
135 I = np.array([[1., 0.], [0., 1.]], dtype=np.float64)
136 return np.dot(np.linalg.inv(I + d * self.QTens), self.QTens)
137
139 '''Compute the complex parameters q1 and q2 and theta of beam.
140
141 Returns a dictionary with keys:
142 '1': q1 [complex]
143 '2': q2 [complex]
144 'theta': theta [float]
145 '''
146 d = float(d)
147
148 a = self.Q(d)[0][0]
149 b = self.Q(d)[0][1]
150 d = self.Q(d)[1][1]
151
152 q1inv = .5*(a + d + np.sqrt((a - d)**2. + 4.*b**2.))
153 q2inv = .5*(a + d - np.sqrt((a - d)**2. + 4.*b**2.))
154
155 if q1inv == q2inv:
156 theta = 0.
157 else:
158 theta = .5*np.arcsin(2.*b/(q1inv - q2inv))
159
160 try:
161 a = 1/q1inv
162 except ZeroDivisionError:
163 a = np.inf
164 try:
165 b = 1/q2inv
166 except ZeroDivisionError:
167 b = np.inf
168 return {'1': a, '2': b, 'theta':theta}
169
170 - def ROC(self, dist = 0.):
171 '''Return the tuple of ROC of the beam.
172
173 '''
174 dist = float(dist)
175 Q = self.QParam(dist)
176 try:
177 a = 1./np.real(1./Q['1'])
178 except FloatingPointError:
179 a = np.inf
180 try:
181 b = 1./np.real(1./Q['2'])
182 except FloatingPointError:
183 b = np.inf
184
185 return (a, b)
186
188 '''Return the tuple of positions of the waists of the beam along Dir.
189
190 '''
191 Q = self.QParam(0.)
192 return (-np.real(Q['1']), -np.real(Q['2']))
193
195 '''Return the tuple of Rayleigh ranges of the beam.
196
197 '''
198 Q = self.QParam()
199 return (np.abs(np.imag(Q['1'])), np.abs(np.imag(Q['2'])))
200
201 - def width(self, d = 0.):
202 '''Return the tuple of beam widths.
203
204 '''
205 d = float(d)
206 lam = self.Wl/self.N
207 zR = self.rayleigh()
208 D = self.waistPos()
209
210 return (np.sqrt((lam/pi)*((d - D[0])**2. + zR[0]**2.)/zR[0]) ,
211 np.sqrt((lam/pi)*((d - D[1])**2. + zR[1]**2.)/zR[1]))
212
214 '''Return a tuple with the waist sizes in x and y.
215
216 '''
217 pos = self.waistPos()
218 return (self.width(pos[0])[0], self.width(pos[1])[1] )
219
220 - def gouy(self, d = 0.):
221 '''Return the tuple of Gouy phases.
222
223 '''
224 d = float(d)
225 zR = self.rayleigh()
226 WDist = self.waistPos()
227 return (np.arctan((d-WDist[0])/zR[0]),
228 np.arctan((d-WDist[1])/zR[1]))
229
230 - def translate(self, X = 0., Y = 0., Z = 0.):
231 '''Move the beam to (current position + (X, Y, Z)).
232
233 X, Y, Z: components of the translation vector.
234
235 No return value.
236 '''
237 self.Pos = self.Pos + np.array([X, Y, Z], dtype = np.float64)
238
239 -def userGaussianBeam(Wx = 1.e-3, Wy = 1.e-3, WDistx = 0., WDisty = 0.,
240 Wl = 1064.e-9, P = 1., X = 0., Y = 0., Z = 0.,
241 Theta = pi/2., Phi = 0., Alpha = 0.,
242 Ref = None):
243 '''Constructor used for user inputed beams, separated from the class
244 initializer because the internal state of a beam is very different from
245 the input of this user-defined beam.
246
247 Input parameters are processed to make arguments for the class
248 contructor and then the corresponding beam is returned.
249 '''
250
251
252 P = float(P)
253 Pos = np.array([X, Y, Z], dtype = np.float64)
254 Dir = np.array([np.sin(Theta) * np.cos(Phi),
255 np.sin(Theta) * np.sin(Phi),
256 np.cos(Theta)], dtype = np.float64)
257
258
259 Alpha = float(Alpha)
260 (u1,v1) = geometry.basis(Dir)
261 v = np.cos(Alpha)*v1 - np.sin(Alpha)*u1
262 u = np.cos(Alpha)*u1 + np.sin(Alpha)*v1
263
264
265 Wl = float(Wl)
266 Wx = float(Wx)
267 Wy = float(Wy)
268 qx = complex(- float(WDistx) + 1.j * pi*Wx**2./Wl )
269 qy = complex(- float(WDisty) + 1.j * pi*Wy**2./Wl )
270 QTens = np.array([[1./qx, 0.],[0., 1./qy]],
271 dtype = np.complex64)
272
273 if Ref is None:
274 Ref = "Beam" + str(GaussianBeam.BeamCount)
275 else:
276 Ref = Ref
277
278 return GaussianBeam(Q = QTens, N = 1., Wl = Wl, P = P,
279 Pos = Pos, Dir = Dir,
280 Ux = u, Uy = v, Ref = Ref, OptDist = 0.,
281 Length = 0., StrayOrder = 0, Optic = 'Laser', Face = 'Out')
282