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