1 | /* ======================================================================== *\
|
---|
2 | !
|
---|
3 | ! *
|
---|
4 | ! * This file is part of CheObs, the Modular Analysis and Reconstruction
|
---|
5 | ! * Software. It is distributed to you in the hope that it can be a useful
|
---|
6 | ! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
|
---|
7 | ! * It is distributed WITHOUT ANY WARRANTY.
|
---|
8 | ! *
|
---|
9 | ! * Permission to use, copy, modify and distribute this software and its
|
---|
10 | ! * documentation for any purpose is hereby granted without fee,
|
---|
11 | ! * provided that the above copyright notice appears in all copies and
|
---|
12 | ! * that both that copyright notice and this permission notice appear
|
---|
13 | ! * in supporting documentation. It is provided "as is" without express
|
---|
14 | ! * or implied warranty.
|
---|
15 | ! *
|
---|
16 | !
|
---|
17 | !
|
---|
18 | ! Author(s): Thomas Bretz, 6/2019 <mailto:tbretz@physik.rwth-aachen.de>
|
---|
19 | !
|
---|
20 | ! Copyright: CheObs Software Development, 2000-2009
|
---|
21 | !
|
---|
22 | !
|
---|
23 | \* ======================================================================== */
|
---|
24 |
|
---|
25 | //////////////////////////////////////////////////////////////////////////////
|
---|
26 | //
|
---|
27 | // MFresnelLens
|
---|
28 | //
|
---|
29 | //////////////////////////////////////////////////////////////////////////////
|
---|
30 | #include "MFresnelLens.h"
|
---|
31 |
|
---|
32 | #include <fstream>
|
---|
33 | #include <errno.h>
|
---|
34 |
|
---|
35 | #include "MQuaternion.h"
|
---|
36 |
|
---|
37 | #include "MLog.h"
|
---|
38 | #include "MLogManip.h"
|
---|
39 |
|
---|
40 | ClassImp(MFresnelLens);
|
---|
41 |
|
---|
42 | using namespace std;
|
---|
43 |
|
---|
44 | // --------------------------------------------------------------------------
|
---|
45 | //
|
---|
46 | // Default constructor
|
---|
47 | //
|
---|
48 | MFresnelLens::MFresnelLens(const char *name, const char *title)
|
---|
49 | {
|
---|
50 | fName = name ? name : "MFresnelLens";
|
---|
51 | fTitle = title ? title : "Parameter container storing a collection of several mirrors (reflector)";
|
---|
52 |
|
---|
53 | fMaxR = 55/2.;
|
---|
54 | }
|
---|
55 |
|
---|
56 | // --------------------------------------------------------------------------
|
---|
57 | //
|
---|
58 | // Return the total Area of all mirrors. Note, that it is recalculated
|
---|
59 | // with any call.
|
---|
60 | //
|
---|
61 | Double_t MFresnelLens::GetA() const
|
---|
62 | {
|
---|
63 | return fMaxR*fMaxR*TMath::Pi();
|
---|
64 | }
|
---|
65 |
|
---|
66 | // --------------------------------------------------------------------------
|
---|
67 | //
|
---|
68 | // Check with a rough estimate whether a photon can hit the reflector.
|
---|
69 | //
|
---|
70 | Bool_t MFresnelLens::CanHit(const MQuaternion &p) const
|
---|
71 | {
|
---|
72 | // p is given in the reflectory coordinate frame. This is meant as a
|
---|
73 | // fast check without lengthy calculations to omit all photons which
|
---|
74 | // cannot hit the reflector at all
|
---|
75 | return p.R2()<fMaxR*fMaxR;
|
---|
76 | }
|
---|
77 |
|
---|
78 | struct Groove
|
---|
79 | {
|
---|
80 | double fTheta;
|
---|
81 | double fPeakZ;
|
---|
82 |
|
---|
83 | double fTanTheta;
|
---|
84 | double fTanTheta2[3];
|
---|
85 |
|
---|
86 | Groove() { }
|
---|
87 | Groove(double theta, double z) : fTheta(theta), fPeakZ(z)
|
---|
88 | {
|
---|
89 | fTanTheta = tan(theta);
|
---|
90 |
|
---|
91 | fTanTheta2[0] = fTanTheta*fTanTheta;
|
---|
92 | fTanTheta2[1] = fTanTheta*fTanTheta * fPeakZ;
|
---|
93 | fTanTheta2[2] = fTanTheta*fTanTheta * fPeakZ*fPeakZ;
|
---|
94 | }
|
---|
95 | };
|
---|
96 |
|
---|
97 | double CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Groove &g, const double &fThickness)
|
---|
98 | {
|
---|
99 | // p is the position in the plane of the lens (px, py, 0, t)
|
---|
100 | // u is the direction of the photon (ux, uy, dz, dt)
|
---|
101 |
|
---|
102 | // Assuming a cone with peak at z and opening angle theta
|
---|
103 |
|
---|
104 | // Radius at distance z+dz from the tip and at distance r from the axis of the cone
|
---|
105 | // r = (z+dz) * tan(theta)
|
---|
106 |
|
---|
107 | // Defining
|
---|
108 | // zc := z+dz
|
---|
109 |
|
---|
110 | // Surface of the cone
|
---|
111 | // (X) ( tan(theta) * sin(phi) )
|
---|
112 | // (Y) = zc * ( tan(theta) * cos(phi) )
|
---|
113 | // (Z) ( 1 )
|
---|
114 |
|
---|
115 | // Line
|
---|
116 | // (X) (u.x) (p.x)
|
---|
117 | // (Y) = dz * (u.y) + (p.y)
|
---|
118 | // (Z) (u.z) ( 0 )
|
---|
119 |
|
---|
120 | // Equality
|
---|
121 | //
|
---|
122 | // X^2+Y^2 = r^2
|
---|
123 | //
|
---|
124 | // (dz*u.x+p.x)^2 + (dz*u.y+p.y)^2 = (z+dz)^2 * tan(theta)^2
|
---|
125 | //
|
---|
126 | // dz^2*ux^2 + px^2 + 2*dz*ux*px + dz^2*uy^2 + py^2 + 2*dz*uy*py = (z^2*tan(theta)^2 + dz^2*tan(theta)^2 + 2*z*dz*tan(theta)^2
|
---|
127 | //
|
---|
128 | // dz^2*(ux^2 + uy^2 - tan(theta)^2) + 2*dz*(ux*px + uy*py - z*tan(theta)^2) + (px^2+py^2 - z^2*tan(theta)^2) = 0
|
---|
129 |
|
---|
130 | // t := tan(theta)
|
---|
131 | // a := ux^2 + uy^2 - t^2
|
---|
132 | // b/2 := ux*px + uy*py - z *t^2 := B
|
---|
133 | // c := px^2 + py^2 - z^2*t^2
|
---|
134 |
|
---|
135 | // dz = [ -b +- sqrt(b^2 - 4*a*c) ] / [2*a]
|
---|
136 | // dz = [ -B +- sqrt(B^2 - a*c) ] / a
|
---|
137 |
|
---|
138 | const double a = u.R2() - g.fTanTheta2[0];
|
---|
139 | const double B = u.XYvector()*p.XYvector() - g.fTanTheta2[1];
|
---|
140 | const double c = p.R2() - g.fTanTheta2[2];
|
---|
141 |
|
---|
142 | const double B2 = B*B;
|
---|
143 | const double ac = a*c;
|
---|
144 |
|
---|
145 | if (B2<ac)
|
---|
146 | return 0;
|
---|
147 |
|
---|
148 | const double sqrtBac = sqrt(B2 - a*c);
|
---|
149 |
|
---|
150 | if (a==0)
|
---|
151 | return 0;
|
---|
152 |
|
---|
153 | const double dz[2] =
|
---|
154 | {
|
---|
155 | (sqrtBac+B) / a,
|
---|
156 | (sqrtBac-B) / a
|
---|
157 | };
|
---|
158 |
|
---|
159 | // Only one dz[i] can be within the lens (due to geometrical reasons
|
---|
160 |
|
---|
161 | if (dz[0]<0 && dz[0]>fThickness)
|
---|
162 | return dz[0];
|
---|
163 |
|
---|
164 | if (dz[1]<0 && dz[1]>fThickness)
|
---|
165 | return dz[1];
|
---|
166 |
|
---|
167 | return 0;
|
---|
168 | }
|
---|
169 |
|
---|
170 | double CalcRefractiveIndex(double lambda)
|
---|
171 | {
|
---|
172 | // https://refractiveindex.info/?shelf=organic&book=poly(methyl_methacrylate)&page=Szczurowski
|
---|
173 |
|
---|
174 | // n^2-1=\frac{0.99654λ^2}{λ^2-0.00787}+\frac{0.18964λ^2}{λ^2-0.02191}+\frac{0.00411λ^2}{λ^2-3.85727}
|
---|
175 |
|
---|
176 | const double l2 = lambda*lambda;
|
---|
177 |
|
---|
178 | const double c0 = 0.99654/(1-0.00787e6/l2);
|
---|
179 | const double c1 = 0.18964/(1-0.02191e6/l2);
|
---|
180 | const double c2 = 0.00411/(1-3.85727e6/l2);
|
---|
181 |
|
---|
182 | return sqrt(1+c0+c1+c2);
|
---|
183 | }
|
---|
184 |
|
---|
185 | bool ApplyRefraction(MQuaternion &u, const TVector3 &n, const double &n1, const double &n2)
|
---|
186 | {
|
---|
187 | // From: https://stackoverflow.com/questions/29758545/how-to-find-refraction-vector-from-incoming-vector-and-surface-normal
|
---|
188 | // Note that as a default u is supposed to be normalized such that z=1!
|
---|
189 |
|
---|
190 | // u: incoming direction (normalized!)
|
---|
191 | // n: normal vector of surface (pointing back into the old medium?)
|
---|
192 | // n1: refractive index of old medium
|
---|
193 | // n2: refractive index of new medium
|
---|
194 |
|
---|
195 | // V_refraction = r*V_incedence + (rc - sqrt(1- r^2 (1-c^2)))n
|
---|
196 | // r = n1/n2
|
---|
197 | // c = -n dot V_incedence.
|
---|
198 |
|
---|
199 | // The vector should be normalized already
|
---|
200 | // u.NormalizeVector();
|
---|
201 |
|
---|
202 | const double r = n2/n1;
|
---|
203 | const double c = n*u.fVectorPart;
|
---|
204 |
|
---|
205 | const double rc = r*c;
|
---|
206 |
|
---|
207 | const double R = 1 - r*r + rc*rc; // = 1 - (r*r*(1-c*c));
|
---|
208 |
|
---|
209 | // What is this? Total reflection?
|
---|
210 | if (R<0)
|
---|
211 | return false;
|
---|
212 |
|
---|
213 | const double v = rc + sqrt(R);
|
---|
214 |
|
---|
215 | u.fVectorPart = r*u.fVectorPart - v*n;
|
---|
216 |
|
---|
217 | return true;
|
---|
218 | }
|
---|
219 |
|
---|
220 | Int_t MFresnelLens::ExecuteOptics(MQuaternion &p, MQuaternion &u, const Short_t &wavelength) const
|
---|
221 | {
|
---|
222 | // Corsika Coordinates are in cm!
|
---|
223 |
|
---|
224 | const double D = 54.92; // [cm] Diameter
|
---|
225 | const double F = 50.21; // [cm] focal length (see also MGeomCamFAMOUS!)
|
---|
226 |
|
---|
227 | const double R = D/2; // [cm] radius of lens
|
---|
228 | const double w = 0.0100; // [cm] Width of a single groove
|
---|
229 | const double H = 0.25; // [cm] Thickness (from pdf)
|
---|
230 |
|
---|
231 | // Focal length is given at this wavelength
|
---|
232 | const double n0 = CalcRefractiveIndex(546);
|
---|
233 |
|
---|
234 | const double n = CalcRefractiveIndex(wavelength==0 ? 546 : wavelength);
|
---|
235 |
|
---|
236 | // Ray has missed the lens
|
---|
237 | if (p.R()>R)
|
---|
238 | return -1;
|
---|
239 |
|
---|
240 | const int nth = TMath::FloorNint(p.R()/w); // Ray will hit the nth groove
|
---|
241 |
|
---|
242 | const double r0 = nth *w; // Lower radius of nth groove
|
---|
243 | const double r1 = (nth+1) *w; // Upper radius of nth groove
|
---|
244 | const double rc = (nth+0.5)*w; // Center of nth groove
|
---|
245 |
|
---|
246 | // FIXME: Do we have to check the nth-1 and nth+1 groove as well?
|
---|
247 |
|
---|
248 | // Angle of grooves
|
---|
249 | // https://shodhganga.inflibnet.ac.in/bitstream/10603/131007/13/09_chapter%202.pdf
|
---|
250 |
|
---|
251 | // This might be a better reference (Figure 1 Variant 1)
|
---|
252 | // https://link.springer.com/article/10.3103/S0003701X13010064
|
---|
253 |
|
---|
254 | // I suggest we just do the calculation ourselves (but we need to
|
---|
255 | // find out how our lens looks like)
|
---|
256 | // Not 100% sure, but I think we have SC943
|
---|
257 | // https://www.orafol.com/en/europe/products/optic-solutions/productlines#pl1
|
---|
258 |
|
---|
259 | // From the web-page:
|
---|
260 | // Positive Fresnel Lenses ... are usually corrected for spherical aberration.
|
---|
261 |
|
---|
262 | // sin(omega) = R / sqrt(R^2+f^2)
|
---|
263 | // tan(alpha) = sin(omega) / [ 1 - sqrt(n^2-sin(omega)^2) ]
|
---|
264 |
|
---|
265 | const double so = rc / sqrt(rc*rc + F*F);
|
---|
266 | const double alpha = atan(so / (1-sqrt(n0*n0 - so*so))); // alpha<0, Range [0deg; -50deg]
|
---|
267 |
|
---|
268 | // Tim Niggemann:
|
---|
269 | // The surface of the lens follows the shape of a parabolic lens to compensate spherical aberration
|
---|
270 | // Draft angle: psi(r) = 3deg + r * 0.0473deg/mm
|
---|
271 |
|
---|
272 | const double psi = (3 + r0*4.73e-3)*TMath::DegToRad(); // Range [0deg; 15deg]
|
---|
273 |
|
---|
274 | // Find dw value of common z-value
|
---|
275 | //
|
---|
276 | // tan(90-psi) = z/dw
|
---|
277 | // tan(alpha) = -z/(w-dw)
|
---|
278 | //
|
---|
279 | // -> dw = w/(1-tan(90-psi)/tan(alpha))
|
---|
280 |
|
---|
281 | // In a simplified world, all photons which hit the draft surface get lost
|
---|
282 | // FIXME: This needs proper calculation in the same manner than for the main surface
|
---|
283 | const double dw = w/(1-tan(TMath::Pi()/2-psi)/tan(alpha));
|
---|
284 | if (p.R()>r1-dw)
|
---|
285 | return -1;
|
---|
286 |
|
---|
287 | // theta peak_z
|
---|
288 | const Groove g(TMath::Pi()/2 + alpha, -r0*tan(alpha));
|
---|
289 |
|
---|
290 | // Calculate the insersection between the ray and the cone and z between 0 and -H
|
---|
291 | const double dz = CalcIntersection(p, u, g, -H);
|
---|
292 |
|
---|
293 | // No groove was hit
|
---|
294 | if (dz>=0)
|
---|
295 | return -1;
|
---|
296 |
|
---|
297 | // Propagate to the hit point at z=dz (dz<0)
|
---|
298 | p.PropagateZ(u, dz);
|
---|
299 |
|
---|
300 | // Check where the ray has hit
|
---|
301 | // If the ray has not hit within the right radius.. reject
|
---|
302 | if (p.R()<=r0 || p.R()>r1)
|
---|
303 | return -1;
|
---|
304 |
|
---|
305 | // Normal vector of the surface at the hit point
|
---|
306 | // FIXME: Surface roughness?
|
---|
307 | TVector3 norm;;
|
---|
308 | norm.SetMagThetaPhi(1, alpha, p.XYvector().Phi());
|
---|
309 |
|
---|
310 | // Apply refraction at lens entrance (change directional vector)
|
---|
311 | // FIXME: Surace roughness
|
---|
312 | if (!ApplyRefraction(u, norm, 1, n))
|
---|
313 | return -1;
|
---|
314 |
|
---|
315 | // Propagate ray until bottom of lens
|
---|
316 | const double v = u.fRealPart; // c changes in the medium
|
---|
317 | u.fRealPart = n*v;
|
---|
318 | p.PropagateZ(u, -H);
|
---|
319 | u.fRealPart = v; // Set back to c
|
---|
320 |
|
---|
321 | // Apply refraction at lens exit (change directional vector)
|
---|
322 | // Normal surface (bottom of lens)
|
---|
323 | // FIXME: Surace roughness
|
---|
324 | if (!ApplyRefraction(u, TVector3(0, 0, 1), n, 1))
|
---|
325 | return -1;
|
---|
326 |
|
---|
327 | // To make this consistent with a mirror system,
|
---|
328 | // we now change our coordinate system
|
---|
329 | // Rays from the lens to the camera are up-going (positive sign)
|
---|
330 | u.fVectorPart.SetZ(-u.Z());
|
---|
331 |
|
---|
332 | // In the datasheet, it looks as if F is calculated
|
---|
333 | // towards the center of the lens.
|
---|
334 | p.fVectorPart.SetZ(-H/2);
|
---|
335 |
|
---|
336 | return nth; // Keep track of groove index
|
---|
337 | }
|
---|