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