1 '''Defines the Mirror class for theia.'''
2
3
4
5
6
7
8
9
10
11
12 import numpy as np
13 from ..helpers import geometry, settings
14 from ..helpers.units import deg, cm, pi
15 from .optic import Optic
16 from .beam import GaussianBeam
17
19 '''
20
21 Mirror class.
22
23 This class represents semi reflective mirrors composed of two faces (HR, AR)
24 and with a wedge angle. These are the objects with which the beams will
25 interqct during the ray tracing. Please see the documentation for details
26 on the geometric construction of these mirrors.
27
28 *=== Attributes ===*
29 SetupCount (inherited): class attribute, counts all setup components.
30 [integer]
31 OptCount (inherited): class attribute, counts optical components. [string]
32 HRCenter (inherited): center of the 'chord' of the HR surface. [3D vector]
33 HRNorm (inherited): unitary normal to the 'chord' of the HR (always pointing
34 towards the outside of the component). [3D vector]
35 Thick (inherited): thickness of the optic, counted in opposite direction to
36 HRNorm. [float]
37 Dia (inherited): diameter of the component. [float]
38 Name (inherited): name of the component. [string]
39 Ref (inherited): reference string (for keeping track with the lab). [string]
40 ARCenter (inherited): center of the 'chord' of the AR surface. [3D vector]
41 ARNorm (inherited): unitary normal to the 'chord' of the AR (always pointing
42 towards the outside of the component). [3D vector]
43 N (inherited): refraction index of the material. [float]
44 HRK, ARK (inherited): curvature of the HR, AR surfaces. [float]
45 HRr, HRt, ARr, ARt (inherited): power reflectance and transmission
46 coefficients of the HR and AR surfaces. [float]
47 KeepI (inherited): whether of not to keep data of rays for interference
48 calculations on the HR. [boolean]
49 Wedge: wedge angle of the mirror, please refer to the documentation for
50 detaild on the geometry of mirrors and their implementation here.
51 [float]
52 Alpha: rotation alngle used in the geometrical construction of the mirror
53 (see doc, it is the amgle between the projection of Ex on the AR plane
54 and the vector from ARCenter to the point where the cylinder and the AR
55 face meet). [float]
56
57 **Note**: the curvature of any surface is positive for a concave surface
58 (coating inside the sphere).
59 Thus kurv*HRNorm/|kurv| always points to the center
60 of the sphere of the surface, as is the convention for the lineSurfInter of
61 geometry module. Same for AR.
62
63 ******* HRK > 0 and ARK > 0 ******* HRK > 0 and ARK < 0
64 ***** ******** and |ARK| > |HRK|
65 H***A H*********A
66 ***** ********
67 ******* *******
68
69 '''
70
71 - def __init__(self, Wedge = 0., Alpha = 0., X = 0. ,Y = 0., Z = 0.,
72 Theta = pi/2., Phi = 0., Diameter = 10.e-2,
73 HRr = .99, HRt = .01, ARr = .1, ARt = .9,
74 HRK = 0.01, ARK = 0, Thickness = 2.e-2,
75 N = 1.4585, KeepI = False, Name = 'Mirror', Ref = None):
76 '''Mirror initializer.
77
78 Parameters are the attributes and the angles theta and phi are spherical
79 coordinates of HRNorm.
80
81 Returns a mirror.
82
83 '''
84
85 self.Wedge = float(Wedge)
86 self.Alpha = float(Alpha)
87 Theta = float(Theta)
88 Phi = float(Phi)
89 Diameter = float(Diameter)
90 Thickness = float(Thickness)
91
92
93 HRNorm = np.array([np.sin(Theta)*np.cos(Phi),
94 np.sin(Theta) * np.sin(Phi),
95 np.cos(Theta)], dtype = np.float64)
96
97 HRCenter = np.array([X, Y, Z], dtype = np.float64)
98
99
100 ARCenter = HRCenter\
101 - (Thickness + .5*np.tan(self.Wedge)*Diameter)*HRNorm
102
103 a,b = geometry.basis(HRNorm)
104 ARNorm = -np.cos(self.Wedge) * HRNorm\
105 + np.sin(self.Wedge)*(np.cos(self.Alpha) * a\
106 + np.sin(self.Alpha) * b)
107
108 super(Mirror, self).__init__(ARCenter = ARCenter, ARNorm = ARNorm,
109 N = N, HRK = HRK, ARK = ARK, ARr = ARr, ARt = ARt, HRr = HRr, HRt = HRt,
110 KeepI = KeepI, HRCenter = [X, Y, Z], HRNorm = HRNorm,
111 Thickness = Thickness, Diameter = Diameter, Name = Name, Ref = Ref)
112
113
114 if settings.warning:
115 self.geoCheck("mirror")
116
118 '''Returns the list of lines necessary to print the object.
119 '''
120 ans = []
121 ans.append("Mirror: " + self.Name + " (" + str(self.Ref) + ") {")
122 ans.append("Thick: " + str(self.Thick/cm) + "cm")
123 ans.append("Diameter: " + str(self.Dia/cm) + "cm")
124 ans.append("Wedge: " + str(self.Wedge/deg) + "deg")
125 ans.append("Alpha: " + str(self.Alpha/deg) + "deg")
126 ans.append("HRCenter: " + str(self.HRCenter))
127 sph = geometry.rectToSph(self.HRNorm)
128 ans.append("HRNorm: (" + str(sph[0]/deg) + ', ' \
129 + str(sph[1]/deg) + ')deg')
130 ans.append("Index: " + str(self.N))
131 ans.append("HRKurv, ARKurv: " + str(self.HRK) + ", " + str(self.ARK))
132 ans.append("HRr, HRt, ARr, ARt: " +str(self.HRr) + ", " + str(self.HRt)\
133 + ", " + str(self.ARr) + ", " + str(self.ARt) )
134 ans.append("}")
135
136 return ans
137
139 '''Determine if a beam hits the Optic.
140
141 This is a function for mirrors, using their geometrical
142 attributes. This uses the line***Inter functions from the geometry
143 module to find characteristics of impact of beams on mirrors.
144
145 beam: incoming beam. [GaussianBeam]
146
147 Returns a dictionary with keys:
148 'isHit': whether the beam hits the optic. [boolean]
149 'intersection point': point in space where it is first hit.
150 [3D vector]
151 'face': to indicate which face is first hit, can be 'HR', 'AR' or
152 'side'. [string]
153 'distance': geometrical distance from beam origin to impact. [float]
154
155 '''
156 noInterDict = {'isHit': False,
157 'intersection point': np.array([0., 0., 0.],
158 dtype=np.float64),
159 'face': None,
160 'distance': 0.}
161
162
163 if np.abs(self.HRK) > 0.:
164 HRDict = geometry.lineSurfInter(beam.Pos,
165 beam.Dir, self.HRCenter,
166 self.HRK*self.HRNorm/np.abs(self.HRK),
167 np.abs(self.HRK),
168 self.Dia)
169 else:
170 HRDict = geometry.linePlaneInter(beam.Pos, beam.Dir, self.HRCenter,
171 self.HRNorm, self.Dia)
172
173 if np.abs(self.ARK) > 0.:
174 ARDict = geometry.lineSurfInter(beam.Pos,
175 beam.Dir, self.ARCenter,
176 self.ARK*self.ARNorm/np.abs(self.ARK),
177 np.abs(self.ARK),
178 self.Dia/np.cos(self.Wedge))
179 else:
180 ARDict = geometry.linePlaneInter(beam.Pos, beam.Dir, self.ARCenter,
181 self.ARNorm, self.Dia)
182
183 SideDict = geometry.lineCylInter(beam.Pos, beam.Dir,
184 self.HRCenter, self.HRNorm,
185 self.Thick, self.Dia)
186
187
188 HRDict['face'] = 'HR'
189 ARDict['face'] = 'AR'
190 SideDict['face'] = 'Side'
191
192
193 hitFaces = filter(lambda dic: dic['isHit'], [HRDict, ARDict, SideDict])
194
195 if len(hitFaces) == 0:
196 return noInterDict
197
198 dist = hitFaces[0]['distance']
199 j=0
200
201 for i in range(len(hitFaces)):
202 if hitFaces[i]['distance'] < dist:
203 dist = hitFaces[i]['distance']
204 j=i
205
206 return {'isHit': True,
207 'intersection point': hitFaces[j]['intersection point'],
208 'face': hitFaces[j]['face'],
209 'distance': hitFaces[j]['distance']
210 }
211
212 - def hit(self, beam, order, threshold):
213 '''Compute the refracted and reflected beams after interaction.
214
215 The beams returned are those selected after the order and threshold
216 criterion.
217
218 beam: incident beam. [GaussianBeam]
219 order: maximum strayness of daughter beams, whixh are not returned if
220 their strayness is over this order. [integer]
221 threshold: idem for the power of the daughter beams. [float]
222
223 Returns a dictionary of beams with keys:
224 't': refracted beam. [GaussianBeam]
225 'r': reflected beam. [GaussianBeam]
226
227 '''
228
229 dic = self.isHit(beam)
230 beam.Length = dic['distance']
231 beam.OptDist = beam.N * beam.Length
232
233 if dic['face'] == 'HR':
234 return self.hitHR(beam, dic['intersection point'], order, threshold)
235 elif dic['face'] == 'AR':
236 return self.hitAR(beam, dic['intersection point'], order, threshold)
237 else:
238 return self.hitSide(beam)
239
240 - def hitHR(self, beam, point, order, threshold):
241 '''Compute the daughter beams after interaction on HR at point.
242
243 beam: incident beam. [GaussianBeam]
244 point: point in space of interaction. [3D vector]
245 order: maximum strayness of daughter beams, whixh are not returned if
246 their strayness is over this order. [integer]
247 threshold: idem for the power of the daughter beams. [float]
248
249 Returns a dictionary of beams with keys:
250 't': refracted beam. [GaussianBeam]
251 'r': reflected beam. [GaussianBeam]
252
253 '''
254
255 ans = {}
256 d = np.linalg.norm(point - beam.Pos)
257
258 if self.HRK == 0.:
259 localNorm = self.HRNorm
260 else:
261 try:
262 theta = np.arcsin(self.Dia * self.HRK/2.)
263 except FloatingPointError:
264 theta = pi/2.
265
266 sphereC = self.HRCenter + np.cos(theta)*self.HRNorm/self.HRK
267 localNorm = sphereC - point
268 localNorm = localNorm/np.linalg.norm(localNorm)
269
270 if np.dot(beam.Dir, localNorm) > 0.:
271 localNorm = - localNorm
272 K = np.abs(self.HRK)
273 else:
274 K = -np.abs(self.HRK)
275
276
277 if np.dot(beam.Dir, self.HRNorm) < 0.:
278
279 n1 = beam.N
280 n2 = self.N
281 else:
282
283 n1 = self.N
284 n2 = 1.
285
286
287 dir2 = geometry.newDir(beam.Dir, localNorm, n1, n2)
288
289
290 if dir2['TR'] and settings.info:
291 print "theia: Info: Total reflection of " + beam.Ref + ' on HR of'\
292 + self.Name + " (" + self.Ref + ")."
293
294
295 if beam.P * self.HRt < threshold or beam.StrayOrder + 1 > order\
296 or dir2['t'] is None:
297 ans['t'] = None
298
299
300 if beam.P * self.HRr < threshold:
301 ans['r'] = None
302
303
304 if len(ans) == 2:
305 if settings.info:
306 print "theia: Info: Reached leaf of tree by interaction ("\
307 + beam.Ref + " on " + self.Ref + ', ' + 'HR).'
308 return ans
309
310
311 if not 'r' in ans:
312 Uxr, Uyr = geometry.basis(dir2['r'])
313 Uzr = dir2['r']
314
315 if not 't' in ans:
316 Uxt, Uyt = geometry.basis(dir2['t'])
317 Uzt = dir2['t']
318
319 Lx, Ly = geometry.basis(localNorm)
320
321
322 C = np.array([[K, 0.], [0, K]])
323 Ki = np.array([[np.dot(beam.U[0], Lx), np.dot(beam.U[0], Ly)],
324 [np.dot(beam.U[1], Lx), np.dot(beam.U[1], Ly)]])
325 Qi = beam.Q(d)
326 Kit = np.transpose(Ki)
327 Xi = np.matmul(np.matmul(Kit, Qi), Ki)
328
329 if not 't' in ans:
330 Kt = np.array([[np.dot(Uxt, Lx), np.dot(Uxt, Ly)],
331 [np.dot(Uyt, Lx), np.dot(Uyt, Ly)]])
332 Ktt = np.transpose(Kt)
333 Ktinv = np.linalg.inv(Kt)
334 Kttinv = np.linalg.inv(Ktt)
335 Xt = (np.dot(localNorm, beam.Dir) -n2*np.dot(localNorm, Uzt)/n1) * C
336 Qt = n1*np.matmul(np.matmul(Kttinv, Xi - Xt), Ktinv)/n2
337
338 if not 'r' in ans:
339 Kr = np.array([[np.dot(Uxr, Lx), np.dot(Uxr, Ly)],
340 [np.dot(Uyr, Lx), np.dot(Uyr, Ly)]])
341 Krt = np.transpose(Kr)
342 Krinv = np.linalg.inv(Kr)
343 Krtinv = np.linalg.inv(Krt)
344 Xr = (np.dot(localNorm, beam.Dir) - np.dot(localNorm, Uzr)) * C
345 Qr = np.matmul(np.matmul(Krtinv, Xi - Xr), Krinv)
346
347
348 if not 'r' in ans:
349 ans['r'] = GaussianBeam(Q = Qr,
350 Pos = point, Dir = Uzr, Ux = Uxr, Uy = Uyr,
351 N = n1, Wl = beam.Wl, P = beam.P * self.HRr,
352 StrayOrder = beam.StrayOrder, Ref = beam.Ref + 'r',
353 Optic = self.Ref, Face = 'HR', Name = "Beam",
354 Length = 0., OptDist = 0.)
355
356 if not 't' in ans:
357 ans['t'] = GaussianBeam(Q = Qt, Pos = point, Name = "Beam",
358 Dir = Uzt, Ux = Uxt, Uy = Uyt, N = n2, Wl = beam.Wl,
359 P = beam.P * self.HRt, StrayOrder = beam.StrayOrder + 1,
360 Ref = beam.Ref + 't', Optic = self.Ref, Face = 'HR',
361 Length = 0., OptDist = 0.)
362
363 return ans
364
365 - def hitAR(self, beam, point, order, threshold):
366 '''Compute the daughter beams after interaction on AR at point.
367
368 beam: incident beam. [GaussianBeam]
369 point: point in space of interaction. [3D vector]
370 order: maximum strayness of daughter beams, which are not returned if
371 their strayness is over this order. [integer]
372 threshold: idem for the power of the daughter beams. [float]
373
374 Returns a dictionary of beams with keys:
375 't': refracted beam. [GaussianBeam]
376 'r': reflected beam. [GaussianBeam]
377
378 '''
379
380 ans = {}
381 d = np.linalg.norm(point - beam.Pos)
382
383 if self.ARK == 0.:
384 localNorm = self.ARNorm
385 else:
386 try:
387 theta = np.arcsin(self.Dia * self.ARK/2.)
388 except FloatingPointError:
389 theta = pi/2.
390 sphereC = self.ARCenter + np.cos(theta)*self.ARNorm/self.ARK
391 localNorm = sphereC - point
392 localNorm = localNorm/np.linalg.norm(localNorm)
393
394 if np.dot(beam.Dir, localNorm) > 0.:
395 localNorm = - localNorm
396 K = np.abs(self.ARK)
397 else:
398 K = - np.abs(self.ARK)
399
400 if np.dot(beam.Dir, self.ARNorm) < 0.:
401
402 n1 = beam.N
403 n2 = self.N
404 else:
405
406 n1 = self.N
407 n2 = 1.
408
409
410 dir2 = geometry.newDir(beam.Dir, localNorm, n1, n2)
411
412
413 if dir2['TR'] and settings.info:
414 print "theia: Info: Total reflection of " + beam.Ref + ' on AR of'\
415 + self.Name + " (" + self.Ref + ")."
416
417
418 if beam.P * self.ARt < threshold or dir2['t'] is None:
419 ans['t'] = None
420
421
422 if beam.P * self.ARr < threshold or beam.StrayOrder + 1 > order:
423 ans['r'] = None
424
425
426 if len(ans) == 2:
427 if settings.info:
428 print "theia: Info: Reached leaf of tree by interaction ("\
429 + beam.Ref + " on " + self.Ref + ', ' + 'AR).'
430 return ans
431
432
433 if not 'r' in ans:
434 Uxr, Uyr = geometry.basis(dir2['r'])
435 Uzr = dir2['r']
436
437 if not 't' in ans:
438 Uxt, Uyt = geometry.basis(dir2['t'])
439 Uzt = dir2['t']
440
441 Lx, Ly = geometry.basis(localNorm)
442
443
444 C = np.array([[K, 0.], [0, K]])
445 Ki = np.array([[np.dot(beam.U[0], Lx), np.dot(beam.U[0], Ly)],
446 [np.dot(beam.U[1], Lx), np.dot(beam.U[1], Ly)]])
447 Qi = beam.Q(d)
448 Kit = np.transpose(Ki)
449 Xi = np.matmul(np.matmul(Kit, Qi), Ki)
450
451 if not 't' in ans:
452 Kt = np.array([[np.dot(Uxt, Lx), np.dot(Uxt, Ly)],
453 [np.dot(Uyt, Lx), np.dot(Uyt, Ly)]])
454 Ktt = np.transpose(Kt)
455 Ktinv = np.linalg.inv(Kt)
456 Kttinv = np.linalg.inv(Ktt)
457 Xt = (np.dot(localNorm, beam.Dir) -n2*np.dot(localNorm, Uzt)/n1) * C
458 Qt = n1*np.matmul(np.matmul(Kttinv, Xi - Xt), Ktinv)/n2
459
460 if not 'r' in ans:
461 Kr = np.array([[np.dot(Uxr, Lx), np.dot(Uxr, Ly)],
462 [np.dot(Uyr, Lx), np.dot(Uyr, Ly)]])
463 Krt = np.transpose(Kr)
464 Krinv = np.linalg.inv(Kr)
465 Krtinv = np.linalg.inv(Krt)
466 Xr = (np.dot(localNorm, beam.Dir) - np.dot(localNorm, Uzr)) * C
467 Qr = np.matmul(np.matmul(Krtinv, Xi - Xr), Krinv)
468
469
470 if not 'r' in ans:
471 ans['r'] = GaussianBeam(Q = Qr, Name = "Beam",
472 Pos = point, Dir = Uzr, Ux = Uxr, Uy = Uyr,
473 N = n1, Wl = beam.Wl, P = beam.P * self.ARr,
474 StrayOrder = beam.StrayOrder + 1, Ref = beam.Ref + 'r',
475 Optic = self.Ref, Face = 'AR',
476 Length = 0., OptDist = 0.)
477
478 if not 't' in ans:
479 ans['t'] = GaussianBeam(Q = Qt, Pos = point,
480 Dir = Uzt, Ux = Uxt, Uy = Uyt, N = n2, Wl = beam.Wl,
481 P = beam.P * self.ARt, StrayOrder = beam.StrayOrder,
482 Ref = beam.Ref + 't', Optic = self.Ref, Face = 'AR',
483 Length = 0., OptDist =0., Name = "Beam")
484
485 return ans
486