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: " + self.Name + " (" + self.Ref + ") " + "{")
108 ans.append("Power: " + str(self.P) + "W/"\
109 +"Index: " + str(self.N)\
110 +"/Wavelength: "+str(self.Wl/nm)+"nm/Length: " + str(self.Length) + "m")
111 ans.append("Order: " + str(self.StrayOrder))
112 ans.append("Origin: " + str(self.Pos))
113
114 sph = geometry.rectToSph(self.Dir)
115 ans.append("Direction: (" + str(sph[0]/deg) + ', ' \
116 + str(sph[1]/deg) + ')deg')
117
118 sphx = geometry.rectToSph(self.U[0])
119 ans.append("Ux: (" + str(sphx[0]/deg) + ', ' \
120 + str(sphx[1]/deg) + ')deg')
121
122 sphy = geometry.rectToSph(self.U[1])
123 ans.append("Uy: (" + str(sphy[0]/deg) + ', ' \
124 + str(sphy[1]/deg) + ')deg')
125 ans.append("Waist Pos: " + str(self.waistPos()) + 'm')
126 ans.append("Waist Size: (" \
127 + str(self.waistSize()[0]/mm) + ', ' + str(self.waistSize()[1]/mm)\
128 + ')mm')
129 ans.append("Rayleigh: " + str(self.rayleigh()) + "m")
130 ans.append("ROC: " + str(self.ROC()))
131 ans.append("}")
132
133 return ans
134
135 - def Q(self, d = 0.):
136 '''Return the Q tensor at a distance d of origin.
137
138 '''
139 d = float(d)
140 I = np.array([[1., 0.], [0., 1.]], dtype=np.float64)
141 return np.matmul(np.linalg.inv(I + d * self.QTens),self.QTens)
142
144 '''Compute the complex parameters q1 and q2 and theta of beam.
145
146 Returns a dictionary with keys:
147 '1': q1 [complex]
148 '2': q2 [complex]
149 'theta': theta [float]
150 '''
151 d = float(d)
152
153 a = self.Q(d)[0][0]
154 b = self.Q(d)[0][1]
155 d = self.Q(d)[1][1]
156
157 q1inv = .5*(a + d + np.sqrt((a - d)**2. + 4.*b**2.))
158 q2inv = .5*(a + d - np.sqrt((a - d)**2. + 4.*b**2.))
159
160 if q1inv == q2inv:
161 theta = 0.
162 else:
163 theta = .5*np.arcsin(2.*b/(q1inv - q2inv))
164
165 try:
166 a = 1/q1inv
167 except ZeroDivisionError:
168 a = np.inf
169 try:
170 b = 1/q2inv
171 except ZeroDivisionError:
172 b = np.inf
173 return {'1': a, '2': b, 'theta':theta}
174
175 - def ROC(self, dist = 0.):
176 '''Return the tuple of ROC of the beam.
177
178 '''
179 dist = float(dist)
180 Q = self.QParam(dist)
181 try:
182 a = 1./np.real(1./Q['1'])
183 except FloatingPointError:
184 a = np.inf
185 try:
186 b = 1./np.real(1./Q['2'])
187 except FloatingPointError:
188 b = np.inf
189
190 return (a, b)
191
193 '''Return the tuple of positions of the waists of the beam along Dir.
194
195 '''
196 Q = self.QParam(0.)
197 return (-np.real(Q['1']), -np.real(Q['2']))
198
200 '''Return the tuple of Rayleigh ranges of the beam.
201
202 '''
203 Q = self.QParam()
204 return (np.abs(np.imag(Q['1'])), np.abs(np.imag(Q['2'])))
205
206 - def width(self, d = 0.):
207 '''Return the tuple of beam widths.
208
209 '''
210 d = float(d)
211 lam = self.Wl/self.N
212 zR = self.rayleigh()
213 D = self.waistPos()
214
215 return (np.sqrt((lam/pi)*((d - D[0])**2. + zR[0]**2.)/zR[0]) ,
216 np.sqrt((lam/pi)*((d - D[1])**2. + zR[1]**2.)/zR[1]))
217
219 '''Return a tuple with the waist sizes in x and y.
220
221 '''
222 pos = self.waistPos()
223 return (self.width(pos[0])[0], self.width(pos[1])[1] )
224
225 - def gouy(self, d = 0.):
226 '''Return the tuple of Gouy phases.
227
228 '''
229 d = float(d)
230 zR = self.rayleigh()
231 WDist = self.waistPos()
232 return (np.arctan((d-WDist[0])/zR[0]),
233 np.arctan((d-WDist[1])/zR[1]))
234
235 - def translate(self, X = 0., Y = 0., Z = 0.):
236 '''Move the beam to (current position + (X, Y, Z)).
237
238 X, Y, Z: components of the translation vector.
239
240 No return value.
241 '''
242 self.Pos = self.Pos + np.array([X, Y, Z], dtype = np.float64)
243
244 -def userGaussianBeam(Wx = 1.e-3, Wy = 1.e-3, WDistx = 0., WDisty = 0.,
245 Wl = 1064.e-9, P = 1., X = 0., Y = 0., Z = 0.,
246 Theta = pi/2., Phi = 0., Alpha = 0.,
247 Ref = None):
248 '''Constructor used for user inputed beams, separated from the class
249 initializer because the internal state of a beam is very different from
250 the input of this user-defined beam.
251
252 Input parameters are processed to make arguments for the class
253 contructor and then the corresponding beam is returned.
254 '''
255
256
257 P = float(P)
258 Pos = np.array([X, Y, Z], dtype = np.float64)
259 Dir = np.array([np.sin(Theta) * np.cos(Phi),
260 np.sin(Theta) * np.sin(Phi),
261 np.cos(Theta)], dtype = np.float64)
262
263
264 Alpha = float(Alpha)
265 (u1,v1) = geometry.basis(Dir)
266 v = np.cos(Alpha)*v1 - np.sin(Alpha)*u1
267 u = np.cos(Alpha)*u1 + np.sin(Alpha)*v1
268
269
270 Wl = float(Wl)
271 Wx = float(Wx)
272 Wy = float(Wy)
273 qx = complex(- float(WDistx) + 1.j * pi*Wx**2./Wl )
274 qy = complex(- float(WDisty) + 1.j * pi*Wy**2./Wl )
275 QTens = np.array([[1./qx, 0.],[0., 1./qy]],
276 dtype = np.complex64)
277
278 if Ref is None:
279 Ref = "Beam" + str(GaussianBeam.BeamCount)
280 else:
281 Ref = Ref
282
283 return GaussianBeam(Q = QTens, N = 1., Wl = Wl, P = P,
284 Pos = Pos, Dir = Dir,
285 Ux = u, Uy = v, Ref = Ref, OptDist = 0.,
286 Length = 0., StrayOrder = 0, Optic = 'Laser', Face = 'Out')
287