source: trunk/Mars/msimreflector/MFresnelLens.cc@ 19646

Last change on this file since 19646 was 19646, checked in by tbretz, 5 years ago
Replaces gLog by *fLog, try-and-error debugging (using d=10 and d=20) what the correct focal distance is, fixed a bug in FindPeak (accessing fGroove[-1] does not work)
File size: 52.2 KB
Line 
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-2019
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MFresnelLens
28//
29// For some details on definitions please refer to
30// https://application.wiley-vch.de/berlin/journals/op/07-04/OP0704_S52_S55.pdf
31//
32// The HAWC's Eye lens is an Orafol SC943
33// https://www.orafol.com/en/europe/products/optic-solutions/productlines#pl1
34//
35// A good description on ray-tracing can be found here
36// https://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf
37//
38//////////////////////////////////////////////////////////////////////////////
39#include "MFresnelLens.h"
40
41#include <fstream>
42#include <errno.h>
43
44#include <TRandom.h>
45
46#include "MQuaternion.h"
47#include "MReflection.h"
48
49#include "MMath.h"
50
51#include "MLog.h"
52#include "MLogManip.h"
53
54ClassImp(MFresnelLens);
55
56using namespace std;
57
58// ==========================================================================
59
60enum exception_t
61{
62 kValidRay = 0,
63
64 kStrayUpgoing,
65 kOutsideRadius,
66 kNoSurfaceFound,
67 kStrayDowngoing,
68 kAbsorbed,
69
70 kFoundSurfaceUnavailable,
71
72 kInvalidOrigin,
73 kTransitionError,
74
75 kEnter = 1000,
76 kLeave = 2000,
77};
78
79enum surface_t
80{
81 kPhotonHasLeft = 0,
82
83 kEntrySurface,
84 kSlopeSurface,
85 kDraftSurface,
86 kExitSurface,
87
88 kMaterial = 5,
89
90 kNoSurface = 9
91};
92
93
94class raytrace_exception : public runtime_error
95{
96protected:
97 int fError;
98 int fOrigin;
99 int fSurface;
100
101public:
102 raytrace_exception(const int &_id, const int &_origin, const int &_surface, const string& what_arg) :
103 runtime_error(what_arg), fError(_id), fOrigin(_origin), fSurface(_surface)
104 {
105 }
106
107 raytrace_exception(const int &_id, const int &_origin, const int &_surface, const char* what_arg) :
108 runtime_error(what_arg), fError(_id), fOrigin(_origin), fSurface(_surface)
109 {
110 }
111
112 int id() const { return fError + fSurface*10 + fOrigin*100; }
113 int error() const { return fError; }
114 int origin() const { return fOrigin; }
115 int surface() const { return fSurface; }
116};
117
118class raytrace_error : public raytrace_exception
119{
120public:
121 raytrace_error(const int &_id, const int &_origin, const int &_surface, const string& what_arg) :
122 raytrace_exception(_id, _origin, _surface, what_arg) { }
123 raytrace_error(const int &_id, const int &_origin, const int &_surface, const char* what_arg) :
124 raytrace_exception(_id, _origin, _surface, what_arg) { }
125};
126class raytrace_info : public raytrace_exception
127{
128public:
129 raytrace_info(const int &_id, const int &_origin, const int &_surface, const string& what_arg) :
130 raytrace_exception(_id, _origin, _surface, what_arg) { }
131 raytrace_info(const int &_id, const int &_origin, const int &_surface, const char* what_arg) :
132 raytrace_exception(_id, _origin, _surface, what_arg) { }
133};
134class raytrace_user : public raytrace_exception
135{
136public:
137 raytrace_user(const int &_id, const int &_origin, const int &_surface, const string& what_arg) :
138 raytrace_exception(_id, _origin, _surface, what_arg) { }
139 raytrace_user(const int &_id, const int &_origin, const int &_surface, const char* what_arg) :
140 raytrace_exception(_id, _origin, _surface, what_arg) { }
141};
142
143// ==========================================================================
144
145
146// --------------------------------------------------------------------------
147//
148// Default constructor
149//
150MFresnelLens::MFresnelLens(const char *name, const char *title) :
151 fPSF(0), fSlopeAbsorption(false), fDraftAbsorption(false),
152 fBottomReflection(true), fDisableMultiEntry(false), fFresnelReflection(true),
153 fMinHits(0), fMaxHits(0)
154{
155 fName = name ? name : "MFresnelLens";
156 fTitle = title ? title : "Parameter container storing a collection of several mirrors (reflector)";
157
158 // Default: Orafol SC943
159
160 DefineLens();
161}
162
163// ==========================================================================
164
165// --------------------------------------------------------------------------
166//
167// Default ORAFOL SC943
168//
169// Focal Length: F = 50.21 cm
170// Diameter: D = 54.92 cm
171// Groove width: w = 0.01 cm
172// Lens thickness: h = 0.25 cm
173//
174// Default wavelength: 546 nm
175//
176void MFresnelLens::DefineLens(double F, double D, double w, double h, double lambda)
177{
178 fR = D/2; // [cm] Lens radius
179 fW = w; // [cm] Width of a single groove
180 fH = h; // [cm] Thickness of lens
181 fF = F; // [cm] focal length (see also MGeomCamFAMOUS!)
182
183 fLambda = lambda;
184
185 fN = MFresnelLens::RefractiveIndex(fLambda); // Lens
186
187 // Velocity of light within the lens material [cm/ns]
188 // FIXME: Note that for the correct conversion in Transmission()
189 // also the speed in the surrounding medium has to be taken correctly
190 // into account (here it is assumed to be air with N=1
191 fVc = fN/(TMath::C()*100/1e9); // cm/ns
192
193 InitGeometry(fR, fW, fN, fF, fH);
194}
195
196// --------------------------------------------------------------------------
197//
198// Precalculate values such as the intersection points inside the grooves,
199// the angle of the slope and draft surface and the corresponding tangents.
200//
201void MFresnelLens::InitGeometry(double maxr, double width, double N0, double F, double d)
202{
203 const uint32_t num = TMath::CeilNint(maxr/width);
204
205 fGrooves.resize(num);
206
207 for (int i=0; i<num; i++)
208 {
209 const double r0 = i*width;
210 const double rc = i*width + width/2;
211 const double r1 = i*width + width;
212
213 // Slope angle of the reflecting surface alpha
214 // Angle of the draft surface psi
215 const double alpha = -MFresnelLens::SlopeAngle(rc, F, N0, d); // w.r.t. x [30]
216 const double psi = MFresnelLens::DraftAngle(r1); // w.r.t. z [ 5]
217
218 const double tan_alpha = tan(alpha);
219 const double tan_psi = tan(psi);
220
221 fGrooves[i].slope.z = r0*tan_alpha;
222 fGrooves[i].draft.z = -r1/tan_psi;
223
224 fGrooves[i].slope.theta = TMath::Pi()/2-alpha; // w.r.t. +z [ 60]
225 fGrooves[i].draft.theta = -psi; // w.r.t. +z [- 5]
226
227 fGrooves[i].slope.tan_theta = tan(fGrooves[i].slope.theta);
228 fGrooves[i].draft.tan_theta = tan(fGrooves[i].draft.theta);
229
230 fGrooves[i].slope.tan_theta2 = fGrooves[i].slope.tan_theta*fGrooves[i].slope.tan_theta;
231 fGrooves[i].draft.tan_theta2 = fGrooves[i].draft.tan_theta*fGrooves[i].draft.tan_theta;
232
233 fGrooves[i].slope.theta_norm = TMath::Pi()/2-fGrooves[i].slope.theta; // [ 30]
234 fGrooves[i].draft.theta_norm = TMath::Pi()/2-fGrooves[i].draft.theta; // [ 95]
235
236 const double dr = width/(tan_alpha*tan_psi+1);
237
238 fGrooves[i].r = r0 + dr;
239
240 const double z = -dr*tan_alpha;
241
242 fGrooves[i].slope.h = z;
243 fGrooves[i].draft.h = z;
244
245 if (z<-fH)
246 *fLog << warn << "Groove " << i << " deeper (" << z << ") than thickness of lens material (" << fH << ")." << endl;
247 }
248
249 fMaxR = (num+1)*width;
250}
251
252// --------------------------------------------------------------------------
253//
254// Reads the transmission curve from a file
255// (tranmission in percent versus wavelength in nanometers)
256//
257// The transmission curve is used to calculate the absorption lengths.
258// Therefore the thickness for which the tranission curve is valid is
259// required (in cm).
260//
261// The conversion can correct for fresnel reflection at the entry and exit
262// surface assuming that the outside material during the measurement was air
263// (n=1.0003) and the material in PMMA. Correction is applied when
264// correction is set to true <default>.
265//
266// If no valid data was read, 0 is returned. -1 is returned if any tranmission
267// value read from the file is >1. If the fresnel correction leads to a value >1,
268// the value is set to 1. The number of valid data points is returned.
269//
270Int_t MFresnelLens::ReadTransmission(const TString &file, float thickness, bool correction)
271{
272 TGraph transmission(file);
273
274 /*
275 double gx_min, gx_max, gy_min, gy_max;
276 absorption.ComputeRange(gx_min, gy_min, gx_max, gy_max);
277 if (lambda<gx_min || lambda>gx_max)
278 {
279 cout << "Invalid wavelength" << endl;
280 return;
281 }*/
282
283 if (transmission.GetN()==0)
284 return 0;
285
286 for (int i=0; i<transmission.GetN(); i++)
287 {
288 // Correct transmission for Fresnel reflection on the surface
289 const double lambda = transmission.GetX()[i];;
290
291 double trans = transmission.GetY()[i]/100;
292 if (trans>1)
293 {
294 *fLog << err << "Transmission larger than 1." << endl;
295 return -1;
296 }
297
298 if (correction)
299 {
300 // Something like this is requried if correction
301 // for optical boundaries is necessary
302 const double n0 = MFresnelLens::RefractiveIndex(lambda);
303
304 // FIXME: Make N_air a variable
305 const double r0 = (n0-1.0003)/(n0+1.0003);
306 const double r2 = r0*r0;
307
308 trans *= (1+r2)*(1+r2);
309
310 if (trans>1)
311 {
312 *fLog << warn << "Transmission at " << lambda << "nm (" << trans << ") after Fresnel correction larger than 1." << endl;
313 trans = 1;
314 }
315 }
316
317 // convert to absorption length (FIMXE: Sanity check)
318 transmission.GetY()[i] = -thickness/log(trans>0.999 ? 0.999 : trans);
319 }
320
321 fAbsorptionLength = MSpline3(transmission);
322
323 return fAbsorptionLength.GetNp();
324}
325
326// ==========================================================================
327
328// --------------------------------------------------------------------------
329//
330// Refractive Index of PMMA, according to
331// https://refractiveindex.info/?shelf=organic&book=poly(methyl_methacrylate)&page=Szczurowski
332//
333// n^2-1=\frac{0.99654 l^2}{l^2-0.00787}+\frac{0.18964 l^2}{l^2-0.02191}+\frac{0.00411 l^2}{l^2-3.85727}
334//
335// Returns the refractive index n as a function of wavelength (in nanometers)
336//
337double MFresnelLens::RefractiveIndex(double lambda)
338{
339 const double l2 = lambda*lambda;
340
341 const double c0 = 0.99654/(1-0.00787e6/l2);
342 const double c1 = 0.18964/(1-0.02191e6/l2);
343 const double c2 = 0.00411/(1-3.85727e6/l2);
344
345 return sqrt(1+c0+c1+c2);
346}
347
348// --------------------------------------------------------------------------
349//
350// A Fresnel lens with parabolic surface calculated with the sagittag
351// function (k=-1) and a correction for the thickness of the lens
352// on the curvature. See also PhD thesis, Tim Niggemann ch. 7.1.1.
353//
354// see also W.J.Smith, Modern Optical Engineering, 2.8 The "Thin Lens"
355// 1/f = (n-1)/radius Eq. 2.36 with thickness t = 0
356// bfl = f Eq. 2.37 and R2 = inf (c2 = 0)
357//
358// Parameters are:
359// The distance from the center r
360// The focal length to be achieved F
361// The refractive index of the outer medium (usually air) n0
362// The refractive index of the lens material (e.g. PMMA) n1
363// The thichness of the lens d
364//
365// r, F and d have to be in the same units.
366//
367// Return the slope angle alpha [rad]. The Slope angle is defined with
368// respect to the plane of the lens. (0 at the center, decreasing
369// with increasing radial distance)
370//
371double MFresnelLens::SlopeAngleParabolic(double r, double F, double n0, double n1, double d)
372{
373 // In the datasheet, it looks as if F is calculated
374 // towards the center of the lens. It seems things are more
375 // consistent if the thickness correction in caluating the
376 // slope angle is omitted and the focal distance is measured
377 // from the entrance of the lens => FIXME: To be checked
378 const double rn = n1/n0;
379 const double c = (rn - 1) * (F + d/rn); // FIXME: try and error with a large d
380 return -atan(r/c);
381
382 // F = 50.21
383 // d= 10 d=20
384 // -: 47 43.7
385 // 0: 53.5 57.0
386 // +: 60.3 70.3
387}
388
389// --------------------------------------------------------------------------
390//
391// A Fresnel lens with an optimized parabolic surface calculated with
392// the sagittag function (k=-1) and fitted coefficients according
393// to Master thesis, Eichler.
394//
395// Note that for this setup other parameters must be fixed
396//
397// Parameters are:
398// The distance from the center r
399//
400// r is in cm.
401//
402// Return the slope angle alpha [rad]. The Slope angle is defined with
403// respect to the plane of the lens. (0 at the center, decreasing
404// with increasing radial distance)
405//
406double MFresnelLens::SlopeAngleAspherical(double r)
407{
408 // Master, Eichler [r/cm]
409 return -atan( r/26.47
410 +2*1.18e-4 * 1e1*r
411 +4*1.34e-9 * 1e3*r*r*r
412 +6*9.52e-15 * 1e5*r*r*r*r*r
413 -8*2.04e-19 * 1e7*r*r*r*r*r*r*r);
414}
415
416// --------------------------------------------------------------------------
417//
418// Ideal angle of the Fresnel surfaces at a distance r from the center
419// to achieve a focal distance F for a positive Fresnel lens made
420// from a material with a refractive index n.
421// A positive Fresnel lens is one which focuses light from infinity
422// (the side with the grooves) to a point (the flat side of the lens).
423//
424// The calculation follows
425// https://shodhganga.inflibnet.ac.in/bitstream/10603/131007/13/09_chapter%202.pdf
426// Here, a thin lens is assumed
427//
428// sin(omega) = r / sqrt(r^2+F^2)
429// tan(alpha) = sin(omega) / [ 1 - sqrt(n^2-sin(omega)^2) ]
430//
431// Return alpha [rad] as a function of the radial distance r, the
432// focal length F and the refractive index n. r and F have to have
433// the same units. The Slope angle is defined with respect to the plane
434// of the lens. (0 at the center, decreasing with increasing radial
435// distance)
436//
437double MFresnelLens::SlopeAngleOptimized(double r, double F, double n)
438{
439 // Use F+d/2
440 double so = r / sqrt(r*r + F*F);
441 return atan(so / (1-sqrt(n*n - so*so))); // alpha<0, Range [0deg; -50deg]
442}
443
444// --------------------------------------------------------------------------
445//
446// Currently calles SlopeAngleParabolic(r, F, 1, n, d)
447//
448double MFresnelLens::SlopeAngle(double r, double F, double n, double d)
449{
450 return SlopeAngleParabolic(r, F, 1.0003, n, d);
451}
452
453
454//
455// Draft angle of the Orafol SC943 According to the thesis of Eichler
456// and NiggemannTim Niggemann:
457//
458// The surface of the lens follows the shape of a parabolic lens to compensate spherical aberration
459// Draft angle: psi(r) = 3deg + r * 0.0473deg/mm
460//
461// The draft angle is returned in radians and is defined w.r.t. to the
462// normal of the lens surface. (almost 90deg at the center,
463// decreasing with increasing radial distance)
464//
465double MFresnelLens::DraftAngle(double r)
466{
467 return (3 + r*0.473)*TMath::DegToRad(); // Range [0deg; 15deg]
468}
469
470// ==========================================================================
471
472// --------------------------------------------------------------------------
473//
474// Return the total Area of all mirrors. Note, that it is recalculated
475// with any call.
476//
477Double_t MFresnelLens::GetA() const
478{
479 return fMaxR*fMaxR*TMath::Pi();
480}
481
482// --------------------------------------------------------------------------
483//
484// Check with a rough estimate whether a photon can hit the reflector.
485//
486Bool_t MFresnelLens::CanHit(const MQuaternion &p) const
487{
488 // p is given in the reflectory coordinate frame. This is meant as a
489 // fast check without lengthy calculations to omit all photons which
490 // cannot hit the reflector at all
491 return p.R2()<fMaxR*fMaxR;
492}
493
494// ==========================================================================
495
496// FIXME: The rays could be 'reflected' inside the material
497// (even though its going out) or vice versa
498static double RandomTheta(double psf)
499{
500 return psf>0 ? MMath::RndmPSF(psf)/2 : 0;
501}
502
503// FIXME: The rays could be 'reflected' inside the material
504// (even though its going out) or vice versa
505static double RandomPhi(double r, double psf)
506{
507 return psf>0 ? MMath::RndmPSF(psf)/2 : 0;
508}
509
510
511// --------------------------------------------------------------------------
512//
513// Calculate the intersection point beweteen a line defined by the position p
514// and the direction u and a cone defined by the object cone.
515//
516// Z: position of peak of cone
517// theta: opening angle of cone
518//
519// Distance r of cone surface at given z from z-axis
520// r_cone(z) = (Z-z)*tan(theta)
521//
522// Equalition of line
523// (x) (p.x) (u.x/u.z)
524// (y) = (p.y) + dz * (u.y/u.z)
525// (z) (p.z) ( 1 )
526//
527// Normalization
528// U.x := u.x/u.z
529// U.y := u.y/u.z
530//
531// Distance of line at given z from z-axis
532// r_line(z) = sqrt(x^2 + y^2) = sqrt( (p.x+dz*u.x)^2 + (p.y+dz*u.y)^2) with dz = z-p.z
533//
534// Equation to be solved
535// r_cone(z) = r_line(z)
536//
537// Solved with wxmaxima:
538//
539// [0] solve((px+(z-pz)*Ux)^2+(py+(z-pz)*Uy)^2= ((Z-z)*t)^2, z);
540//
541// z= (sqrt(((Uy^2+Ux^2)*pz^2+(-2*Uy*py-2*Ux*px-2*Z*Uy^2-2*Z*Ux^2)*pz+py^2+2*Z*Uy*py+px^2+2*Z*Ux*px+Z^2*Uy^2+Z^2*Ux^2)*t^2-Ux^2*py^2+2*Ux*Uy*px*py-Uy^2*px^2)+Z*t^2+(-Uy^2-Ux^2)*pz+Uy*py+Ux*px)/(t^2-Uy^2-Ux^2),
542// z=-(sqrt(((Uy^2+Ux^2)*pz^2+(-2*Uy*py-2*Ux*px-2*Z*Uy^2-2*Z*Ux^2)*pz+py^2+2*Z*Uy*py+px^2+2*Z*Ux*px+Z^2*Uy^2+Z^2*Ux^2)*t^2-Ux^2*py^2+2*Ux*Uy*px*py-Uy^2*px^2)-Z*t^2+( Uy^2+Ux^2)*pz-Uy*py-Ux*px)/(t^2-Uy^2-Ux^2)
543//
544double MFresnelLens::CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Cone &cone) const
545{
546 const double &Z = cone.z;
547
548 const double Ux = u.X()/u.Z();
549 const double Uy = u.Y()/u.Z();
550
551 const double px = p.X();
552 const double py = p.Y();
553 const double pz = p.Z();
554
555 //const double &t = cone.tan_theta;
556 const double &t2 = cone.tan_theta2;
557
558 const double Ur2 = Ux*Ux + Uy*Uy;
559 const double pr2 = px*px + py*py;
560 const double Up2 = Ux*px + Uy*py;
561
562 const double cr2 = Ux*py - Uy*px;
563
564 const double a = t2 - Ur2;
565 const double b = Ur2*pz - Up2 - Z*t2;
566
567 const double h = Z-pz;
568 const double h2 = h*h;
569
570 // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ]
571
572 const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2;
573 if (radix<0)
574 return 0;
575
576 const double sqrt_radix = sqrt(radix);
577
578 const double dz[2] =
579 {
580 (+sqrt_radix - b)/a,
581 (-sqrt_radix - b)/a
582 };
583
584 // Return the closest solution inside the allowed range
585 // which is in the direction of movement
586
587 const double &H = cone.h;
588
589 const bool is_inside0 = dz[0]>=H && dz[0]<0;
590 const bool is_inside1 = dz[1]>=H && dz[1]<0;
591
592 // FIXME: Simplify!
593 if (!is_inside0 && !is_inside1)
594 return 0;
595
596 // Only dz[0] is in the right z-range
597 if (is_inside0 && !is_inside1)
598 {
599 // Check if dz[0] is in the right direction
600 if ((u.Z()>=0 && dz[0]>=p.Z()) ||
601 (u.Z()< 0 && dz[0]< p.Z()))
602 return dz[0];
603
604 return 0;
605 }
606
607 // Only dz[1] is in the right z-range
608 if (!is_inside0 && is_inside1)
609 {
610 // Check if dz[1] is in the right direction
611 if ((u.Z()>=0 && dz[1]>=p.Z()) ||
612 (u.Z()< 0 && dz[1]< p.Z()))
613 return dz[1];
614
615 return 0;
616 }
617
618 /*
619 if (is_inside0^is_inside1)
620 {
621 if (u.Z()>=0)
622 return dz[0]>p.Z() ? dz[0] : dz[1];
623 else
624 return dz[0]<p.Z() ? dz[0] : dz[1];
625 }*/
626
627
628 // dz[0] and dz[1] are in the right range
629 // return the surface which is hit first
630
631 // moving upwards
632 if (u.Z()>=0)
633 {
634 // Both solution could be correct
635 if (dz[0]>=p.Z() && dz[1]>=p.Z())
636 return std::min(dz[0], dz[1]);
637
638 // only one solution can be correct
639 return dz[0]>=p.Z() ? dz[0] : dz[1];
640 }
641 else
642 {
643 // Both solution could be correct
644 if (dz[0]<p.Z() && dz[1]<p.Z())
645 return std::max(dz[0], dz[1]);
646
647 // only one solution can be correct
648 return dz[0]<p.Z() ? dz[0] : dz[1];
649 }
650}
651
652// --------------------------------------------------------------------------
653//
654// Find the peak (draft+slope) which will be hit by the photon which
655// is defined by position p and direction u. ix gives the index of the groove
656// to originate the search from.
657//
658// Returns the index of the groove to which the surface belongs, -1 if no
659// matching surface was found.
660//
661int MFresnelLens::FindPeak(size_t ix, const MQuaternion &p, const MQuaternion &u) const
662{
663 // ---------------------------
664 // check for first groove first
665 if (ix==0)
666 {
667 const auto test = p.fVectorPart + (fGrooves[0].slope.h-p.Z())/u.Z()*u.fVectorPart;
668 if (test.XYvector().Mod()<fGrooves[0].r)
669 return 0;
670 }
671
672 // r = sqrt( (px + t*ux) + (py + t*uy)^2 )
673 // dr/dt = (2*uy*(dz*uy+py)+2*ux*(dz*ux+px))/(2*sqrt((dz*uy+py)^2+(dz*ux+px)^2))
674 // dr/dt = (uy*py + ux*px)/sqrt(py^2+px^2)
675 const bool outgoing = u.X()*p.X() + u.Y()*p.Y() > 0; // r is (at least locally) increasing
676
677 // ---------------------------
678 const double Ux = u.X()/u.Z();
679 const double Uy = u.Y()/u.Z();
680
681 const double px = p.X();
682 const double py = p.Y();
683 const double pz = p.Z();
684
685 const double Ur2 = Ux*Ux + Uy*Uy;
686 const double cr2 = Ux*py - Uy*px;
687 const double pr2 = px*px + py*py;
688 const double Up2 = Ux*px + Uy*py;
689
690 //for (int i=1; i<fGrooves.size(); i++)
691
692 // To speed up the search, search first along the radial moving direction of
693 // the photon. If that was not successfull, try in the opposite direction.
694 // FIXME: This could still fail in some very rare cases, for some extremely flat trajectories
695 for (int j=0; j<2; j++)
696 {
697 const bool first = j==0;
698
699 const int step = outgoing ^ !first ? 1 : -1;
700 const int end = outgoing ^ !first ? fGrooves.size() : 1;
701 const int beg = j==0 ? ix : ix+step;
702
703 if (step==-1 && ix==1) // can not count downwards from here
704 continue;
705
706 for (int i=beg; i!=end; i+=step)
707 {
708 const Groove &groove1 = fGrooves[i-1];
709 const Groove &groove2 = fGrooves[i];
710
711 const double &z1 = groove1.draft.h;
712 const double &z2 = groove2.slope.h;
713
714 const double &r1 = groove1.r;
715 const double &r2 = groove2.r;
716
717 Cone cone;
718 cone.tan_theta = -(r2-r1)/(z2-z1);
719 cone.tan_theta2 = cone.tan_theta*cone.tan_theta;
720 cone.z = z1 + r1/cone.tan_theta;
721
722 const double &Z = cone.z;
723 const double &t2 = cone.tan_theta2;
724
725 const double a = t2 - Ur2;
726 const double b = Ur2*pz - Up2 - Z*t2;
727
728 const double h = Z-pz;
729 const double h2 = h*h;
730
731 // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ]
732
733 const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2;
734 if (radix<0)
735 continue;
736
737 const double sqrt_radix = sqrt(radix);
738
739 const double dz[2] =
740 {
741 (+sqrt_radix - b)/a,
742 (-sqrt_radix - b)/a
743 };
744
745 if (dz[0]>=z2 && dz[0]<=z1)
746 return i;
747
748 if (dz[1]>=z2 && dz[1]<=z1)
749 return i;
750 }
751 }
752
753 return -1;
754}
755
756// --------------------------------------------------------------------------
757//
758// If no transmission was given returns true. Otherwaise calculates the
759// absorption length for a flight time dt in the material and a photon
760// with wavelength lambda. The flight time is converted to a geometrical
761// using the speed of light in the medium.
762//
763// Returns true if the poton passed, false if it was absorbed.
764//
765bool MFresnelLens::Transmission(double dt, double lambda) const
766{
767 if (fAbsorptionLength.GetNp()==0)
768 return true;
769
770 // FIXME: Speed up!
771 const double alpha = fAbsorptionLength.Eval(lambda);
772
773 // We only have the travel time, thus we have to convert back to distance
774 // Note that the transmission coefficients are w.r.t. to geometrical
775 // distance not light-travel distance. Thus the distance has to be corrected
776 // for the corresponding refractive index of the material.
777 const double cm = dt/fVc;
778
779 const double trans = exp(-cm/alpha);
780 return gRandom->Uniform()<trans;
781}
782
783/*
784// surface=0 : incoming ray
785// surface=1 : slope
786// surface=2 : draft
787// surface=3 : bottom
788int MFresnelLens::EnterGroove(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir) const
789{
790 const double rx = pos.R();
791
792 if (surface==3)
793 {
794 //cout << "Bottom as origin invalid" << endl;
795 throw -1;
796
797 }
798 if (rx>=fR)
799 {
800 //cout << "Left the lens radius (enter)" << endl;
801 throw -2;
802 }
803 //if (dir.Z()>0)
804 //{
805 // cout << "Upgoing, outside of the material" << endl;
806 // PropagateZ(pos, dir, dir.Z()>0 ? 3 : -3);
807 // return -1;
808 //}
809
810
811 // Calculate the ordinal number of the groove correpsonding to rx
812 const int ix = TMath::FloorNint(rx/fW);
813
814 // Photons was just injected (test both surfaces) or came from the other surface
815 if (surface==0 || surface==2)
816 {
817 // Get the possible intersection point with the slope angle
818 const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
819
820 // We hit the slope angle
821 if (z1!=0)
822 {
823 // Move photon to new hit position
824 pos.PropagateZ(dir, z1);
825
826 if (fSlopeAbsorption)
827 throw -100;
828
829 // Get the normal vector of the surface which was hit
830 const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
831 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
832
833 // Get the optical transition of the direction vector
834 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
835
836 // Transition was Reflection - try again
837 if (ret==1 || ret==2)
838 return EnterGroove(1, n0, lambda, pos, dir)+1;
839
840 // Transition was Refraction - enter
841 if (ret>=3)
842 return LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
843
844 // Error occured (see ApplyTransition for details)
845 //cout << "ERR[TIR1]" << endl;
846 throw -3;
847 }
848 }
849
850 // Photons was just injected (test both surfaces) or came from the other surface
851 if (surface==0 || surface==1)
852 {
853 const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
854
855 // We hit the draft angle
856 if (z2!=0)
857 {
858 // Move photon to new hit position
859 pos.PropagateZ(dir, z2);
860
861 if (fDraftAbsorption)
862 throw -101;
863
864 // Get the normal vector of the surface which was hit
865 const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
866 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
867
868 // Get the optical transition of the direction vector
869 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
870
871 // Transition was Reflection - try again
872 if (ret==1 || ret==2)
873 return EnterGroove(2, n0, lambda, pos, dir)+1;
874
875 // Transition was Refraction - enter
876 if (ret>=3)
877 return -LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
878
879 // Error occured (see ApplyTransition for details)
880 //cout << "ERR[TIR2]" << endl;
881 throw -4;
882 }
883 }
884
885 if (dir.Z()>0)
886 {
887 //cout << "Upgoing, outside of the material" << endl;
888 //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
889 throw -5;
890 }
891
892 // The ray has left the peak at the bottom(?)
893 //cout << "ERR[N/A]" << endl;
894 throw -6;
895}
896*/
897
898
899// surface=0 : incoming ray
900// surface=1 : slope
901// surface=2 : draft
902// surface=3 : bottom
903int MFresnelLens::EnterGroove(int surface, double n0, MQuaternion &pos, MQuaternion &dir) const
904{
905 const double rx = pos.R();
906
907 if (surface==kExitSurface)
908 throw raytrace_error(kEnter+kInvalidOrigin, surface, -1,
909 "EnterGroove - Bottom as origin invalid");
910
911 if (rx>=fR) // This is an error as the direction vector is now invalid
912 throw raytrace_error(kEnter+kOutsideRadius, surface, -1,
913 "EnterGroove - Surface hit outside allowed radius");
914
915 /*
916 if (dir.Z()>0)
917 return -1;
918 }*/
919
920
921 // FIXME: There is a very tiny chance that a ray hits the same surface twice for
922 // very horizontal rays. Checking this needs to make sure that the same
923 // solution is not just found again.
924
925 // Calculate the ordinal number of the groove correpsonding to rx
926 const int ix = TMath::FloorNint(rx/fW);
927
928 // Photons was just injected (test both surfaces) or came from the other surface
929 if (surface==kEntrySurface || surface==kDraftSurface)
930 {
931 // Get the possible intersection point with the slope angle
932 const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
933
934 // We hit the slope angle
935 if (z1!=0)
936 {
937 // Move photon to new hit position
938 pos.PropagateZ(dir, z1);
939 if (fSlopeAbsorption)
940 throw raytrace_user(kEnter+kAbsorbed, surface, kSlopeSurface,
941 "EnterGroove - Photon absorbed by slope surface");
942
943 // Get the normal vector of the surface which was hit
944 const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
945 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
946
947 // Get the optical transition of the direction vector
948 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
949
950 // Transition was Reflection - try again
951 if (ret==1 || ret==2)
952 return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
953
954 // Transition was Refraction - enter
955 if (ret>=3)
956 return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
957
958 // Error occured (see ApplyTransition for details)
959 throw raytrace_error(kEnter+kTransitionError, surface, kSlopeSurface,
960 "EnterGroove - MOptics::ApplyTransition failed for slope surface");
961 }
962 }
963
964 // Photons was just injected (test both surfaces) or came from the other surface
965 if (surface==kEntrySurface || surface==kSlopeSurface)
966 {
967 const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
968
969 // We hit the draft angle
970 if (z2!=0)
971 {
972 // Move photon to new hit position
973 pos.PropagateZ(dir, z2);
974 if (fDraftAbsorption)
975 throw raytrace_user(kEnter+kAbsorbed, surface, kDraftSurface,
976 "EnterGroove - Photon absorbed by draft surface");
977
978 // Get the normal vector of the surface which was hit
979 const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
980 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
981
982 // Get the optical transition of the direction vector
983 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
984
985 // Transition was Reflection - try again
986 if (ret==1 || ret==2)
987 return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
988
989 // Transition was Refraction - enter
990 if (ret>=3)
991 return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
992
993 // Error occured (see ApplyTransition for details)
994 throw raytrace_error(kEnter+kTransitionError, surface, kDraftSurface,
995 "EnterGroove - MOptics::ApplyTransition failed for draft surface");
996 }
997 }
998
999 if (dir.Z()>0)
1000 {
1001 // We have missed both surfaces and we are upgoing...
1002 // ... ray can be discarded
1003 throw raytrace_info(kEnter+kStrayUpgoing, surface, kNoSurface,
1004 "EnterGroove - Particle is upgoing and has hit no surface");
1005 }
1006
1007 // The ray has left the peak at the bottom(?)
1008 throw raytrace_error(kEnter+kStrayDowngoing, surface, kNoSurface,
1009 "EnterGroove - Particle is downgoing and has hit no surface");
1010}
1011
1012/*
1013// Leave the peak from inside the material, either thought the draft surface or the
1014// slope surface or the bottom connecting the valley of both
1015int MFresnelLens::LeavePeak(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir, double T0) const
1016{
1017 const double rx = pos.R();
1018
1019 if (rx>=fR)
1020 {
1021 //cout << "Left the lens radius (leave)" << endl;
1022 throw -10;
1023 }
1024
1025 if (dir.Z()>0 && surface!=3) // && surface!=4)
1026 {
1027 //cout << "Upgoing, inside of the material" << endl;
1028 //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
1029 throw -11;
1030 }
1031
1032 if (surface!=1 && surface!=2 && surface!=3) // && surface!=4)
1033 {
1034 //cout << "Surface of origin invalid" << endl;
1035 throw -12;
1036 }
1037
1038
1039 // Calculate the ordinal number of the groove correpsonding to rx
1040 const int ix = TMath::FloorNint(rx/fW);
1041
1042 // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
1043
1044 Cone slope = fGrooves[ix].slope;
1045 Cone draft = fGrooves[ix].draft;
1046
1047 const bool is_draft = rx>fGrooves[ix].r;
1048 if (is_draft)
1049 {
1050 // We are in the volume under the draft angle... taking the slope from ix+1
1051 if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
1052 slope = fGrooves[ix+1].slope;
1053 }
1054 else
1055 {
1056 // We are in the volume under the slope angle... taking the draft from ix-1
1057 if (ix>0) // FIXME: Check whether this is correct
1058 draft = fGrooves[ix-1].draft;
1059 }
1060
1061 if (is_draft+1!=surface && (surface==1 || surface==2))
1062 cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
1063
1064 if (surface==3)
1065 {
1066 //cout << "Upgoing, coming from the bottom of the lens" << endl;
1067 // Find out which triangle (peak) the photon is going to enter
1068 // then proceed...
1069 throw -13;
1070 }
1071
1072
1073 // We are inside the material and downgoing, so if we come from a slope surface,
1074 // we can only hit a draft surface after and vice versa
1075 if (is_draft || surface==3)
1076 {
1077 const double z1 = CalcIntersection(pos, dir, slope);
1078
1079 // We hit the slope angle and are currently in the volume under the draft surface
1080 if (z1!=0)
1081 {
1082 // Move photon to new hit position
1083 pos.PropagateZ(dir, z1);
1084
1085 if (fSlopeAbsorption)
1086 throw -200;
1087
1088 // Get the normal vector of the surface which was hit
1089 const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
1090 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1091
1092 // Get the optical transition of the direction vector
1093 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1094
1095 // Transition was Reflection - try again
1096 if (ret==1 || ret==2)
1097 return LeavePeak(1, n0, lambda, pos, dir, T0)+1;
1098
1099 // Transition was Refraction - leave
1100 if (ret>=3)
1101 {
1102 if (!Transmission(pos.T()-T0, lambda))
1103 throw -14;
1104
1105 return EnterGroove(1, n0, lambda, pos, dir)+1;
1106 }
1107
1108 // Error occured (see ApplyTransition for details)
1109 //cout << "ERR[TIR3]" << endl;
1110 throw -15;
1111 }
1112 }
1113
1114 if (!is_draft || surface==3)
1115 {
1116 const double z2 = CalcIntersection(pos, dir, draft);
1117
1118 // We hit the draft angle from the inside and are currently in the volume under the slope angle
1119 if (z2!=0)
1120 {
1121 // Move photon to new hit position
1122 pos.PropagateZ(dir, z2);
1123
1124 if (fDraftAbsorption)
1125 throw -201;
1126
1127 // Get the normal vector of the surface which was hit
1128 const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
1129 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1130
1131 // Get the optical transition of the direction vector
1132 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1133
1134 // Transition was Reflection - try again
1135 if (ret==1 || ret==2)
1136 return LeavePeak(2, n0, lambda, pos, dir, T0)+1;
1137
1138 // Transition was Refraction - leave
1139 if (ret>=3)
1140 {
1141 if (!Transmission(pos.T()-T0, lambda))
1142 throw -16;
1143
1144 return EnterGroove(2, n0, lambda, pos, dir)+1;
1145 }
1146
1147 // Error occured (see ApplyTransition for details)
1148 //cout << "ERR[TIR4]" << endl;
1149 throw -17;
1150 }
1151 }
1152
1153 if (surface==3)// || surface==4)
1154 {
1155 //cout << ix << " Lost bottom reflected ray " << surface << endl;
1156 throw -18;
1157 }
1158
1159 // The ray has left the peak at the bottom
1160
1161 // FIXME: There is a tiny chance to escape to the side
1162 // As there is a slope in the bottom surface of the peak
1163
1164 // Move photon to new hit position
1165 pos.PropagateZ(dir, -fH);
1166
1167 if (pos.R()>fR)
1168 {
1169 //cout << "Left the lens radius (bottom)" << endl;
1170 throw -19;
1171 }
1172
1173 // Get the normal vector of the surface which was hit
1174 const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
1175
1176 // Get the optical transition of the direction vector
1177 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1178
1179 // Transition was Reflection
1180 // (Photon scattered back from the bottom of the lens)
1181 if (ret==1 || ret==2)
1182 return LeavePeak(3, n0, lambda, pos, dir, T0)+1;
1183
1184 // Transition was Refraction
1185 // (Photon left at the bottom of the lens)
1186 if (ret>=3)
1187 {
1188 if (!Transmission(pos.T()-T0, lambda))
1189 throw -20;
1190
1191 return 0;
1192 }
1193
1194 // Error occured (see ApplyTransition for details)
1195 //cout << "ERR[TIR5]" << endl;
1196 throw -21;
1197}*/
1198
1199// Leave the peak from inside the material, either thought the draft surface or the
1200// slope surface or the bottom connecting the valley of both
1201int MFresnelLens::LeavePeak(int surface, double n0, MQuaternion &pos, MQuaternion &dir, double T0) const
1202{
1203 const double rx = pos.R();
1204
1205 if (rx>=fR) // This is an error as the direction vector is now invalid
1206 throw raytrace_error(kLeave+kOutsideRadius, surface, kNoSurface,
1207 "LeavePeak - Surface hit outside allowed radius");
1208
1209 // FIXME: Can we track them further?
1210 if (fDisableMultiEntry && dir.Z()>0 && surface!=3/* && surface!=4*/)
1211 throw raytrace_info(kLeave+kStrayUpgoing, surface, kNoSurface,
1212 "LeavePeak - Particle is upgoing inside the material and does not come from the bottom");
1213
1214 if (surface!=kSlopeSurface && surface!=kDraftSurface && surface!=kExitSurface/* && surface!=4*/)
1215 throw raytrace_error(kLeave+kInvalidOrigin, surface, kNoSurface,
1216 "LeavePeak - Invalid surface of origin");
1217
1218
1219 // Calculate the ordinal number of the groove correpsonding to rx
1220 const int ix = TMath::FloorNint(rx/fW);
1221
1222 // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
1223
1224 Cone slope = fGrooves[ix].slope;
1225 Cone draft = fGrooves[ix].draft;
1226
1227 //if (is_draft+1!=surface && (surface==1 || surface==2))
1228 // cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
1229
1230 const bool is_draft = rx>fGrooves[ix].r;
1231 if (is_draft)
1232 {
1233 // We are in the volume under the draft angle... taking the slope from ix+1
1234 if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
1235 slope = fGrooves[ix+1].slope;
1236 }
1237 else
1238 {
1239 // We are in the volume under the slope angle... taking the draft from ix-1
1240 if (ix>0) // FIXME: Check whether this is correct
1241 draft = fGrooves[ix-1].draft;
1242 }
1243
1244 if (surface==kExitSurface)
1245 {
1246 if (!fBottomReflection)
1247 throw raytrace_user(kLeave+kAbsorbed, surface, kExitSurface,
1248 "LeavePeak - Particle absorbed on the bottom");
1249
1250 const int in = FindPeak(ix, pos, dir);
1251
1252 // This might happen if the ray is very flat and leaving
1253 // the lens before hitting the border boundary of the grooves
1254 if (in<0)
1255 throw raytrace_error(kLeave+kNoSurfaceFound, kExitSurface, kNoSurface,
1256 "LeavePeak - No hit surface found for particle reflected at the bottom");
1257
1258 slope = fGrooves[in].slope;
1259 draft = fGrooves[in==0 ? 0 : in-1].draft;
1260 }
1261
1262 // FIXME: There is a chance that we can hit the same surface twice (for very horizontal rays
1263 // but this requires a proper selection of the hit point
1264
1265 // We are inside the material and downgoing, so if we come from a slope surface,
1266 // we can only hit a draft surface after and vice versa
1267 if (is_draft || surface==kExitSurface)
1268 {
1269 const double z1 = CalcIntersection(pos, dir, slope);
1270
1271 // We hit the slope angle and are currently in the volume under the draft surface
1272 if (z1!=0)
1273 {
1274 // Move photon to new hit position
1275 pos.PropagateZ(dir, z1);
1276
1277 if (fSlopeAbsorption)
1278 throw raytrace_user(kLeave+kAbsorbed, surface, kSlopeSurface,
1279 "LeavePeak - Photon absorbed by slope surface");
1280
1281 // Get the normal vector of the surface which was hit
1282 const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
1283 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1284
1285 // Get the optical transition of the direction vector
1286 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1287
1288 // Transition was Reflection - try again
1289 if (ret==1 || ret==2)
1290 return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, T0)+1;
1291
1292 // Transition was Refraction - leave
1293 if (ret>=3) // Transmission
1294 return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
1295
1296 // Error occured (see ApplyTransition for details)
1297 throw raytrace_error(kLeave+kTransitionError, surface, kSlopeSurface,
1298 "LeavePeak - MOptics::ApplyTransition failed for slope surface");
1299 }
1300 }
1301
1302 if (!is_draft || surface==kExitSurface)
1303 {
1304 const double z2 = CalcIntersection(pos, dir, draft);
1305
1306 // We hit the draft angle from the inside and are currently in the volume under the slope angle
1307 if (z2!=0)
1308 {
1309 // Move photon to new hit position
1310 pos.PropagateZ(dir, z2);
1311
1312 if (fDraftAbsorption)
1313 throw raytrace_user(kLeave+kAbsorbed, surface, kDraftSurface,
1314 "LeavePeak - Photon absorbed by draft surface");
1315
1316 // Get the normal vector of the surface which was hit
1317 const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
1318 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1319
1320 // Get the optical transition of the direction vector
1321 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1322
1323 // Transition was Reflection - try again
1324 if (ret==1 || ret==2)
1325 return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, T0)+1;
1326
1327 // Transition was Refraction - leave
1328 if (ret>=3) // Transmission
1329 return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
1330
1331 // Error occured (see ApplyTransition for details)
1332 //cout << "ERR[TIR4]" << endl;
1333 throw raytrace_error(kLeave+kTransitionError, surface, kDraftSurface,
1334 "LeavePeak - MOptics::ApplyTransition failed for draft surface");
1335 }
1336 }
1337
1338 if (surface==kExitSurface/* || surface==4*/)
1339 throw raytrace_error(kLeave+kFoundSurfaceUnavailable, kExitSurface, is_draft?kSlopeSurface:kDraftSurface,
1340 "LeavePeak - Ray reflected on the bottom did not hit the found surface");
1341
1342 // The ray has left the peak at the bottom
1343
1344 // FIXME: There is a tiny chance to escape to the side
1345 // As there is a slope in the bottom surface of the peak
1346
1347 // FIXME: Theoretically, a ray can hit the same surface twice
1348
1349 // Move photon to new hit position
1350 pos.PropagateZ(dir, -fH);
1351
1352 if (pos.R()>fR)
1353 throw raytrace_info(kLeave+kOutsideRadius, surface, kExitSurface,
1354 "LeavePeak - Hit point at the bottom surface is beyond allowed radius");
1355
1356 // Get the normal vector of the surface which was hit
1357 const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
1358
1359 // Get the optical transition of the direction vector
1360 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1361
1362 // Transition was Reflection
1363 // (Photon scattered back from the bottom of the lens)
1364 if (ret==1 || ret==2)
1365 return -kExitSurface;//LeavePeak(3, n0, lambda, pos, dir, T0)+1;
1366
1367 // Transition was Refraction
1368 // (Photon left at the bottom of the lens)
1369 if (ret>=3) // Transmission
1370 return kPhotonHasLeft;
1371
1372 // Error occured (see ApplyTransition for details)
1373 throw raytrace_error(kLeave+kTransitionError, surface, kExitSurface, "LeavePeak - MOptics::ApplyTransition failed for bottom surface");
1374}
1375
1376
1377// Differences:
1378// Returns a 'reflected' vector at z=0
1379// Does not propagate to z=0 at the beginning
1380Int_t MFresnelLens::ExecuteOptics(MQuaternion &p, MQuaternion &u, const Short_t &wavelength) const
1381{
1382 // Corsika Coordinates are in cm!
1383
1384 const double lambda = wavelength==0 ? fLambda : wavelength;
1385 if (fAbsorptionLength.GetNp()!=0 &&
1386 (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
1387 {
1388 *fLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
1389 return -1;
1390 }
1391
1392 const double n0 = MFresnelLens::RefractiveIndex(lambda);
1393
1394 try
1395 {
1396 int last_surface = EnterGroove(kEntrySurface, n0, p, u);
1397
1398 // last_surface that was hit (photon originates from)
1399 // 0 entrance (Z=0) or exit (Z=-fH) surface
1400 // 1 slope
1401 // 2 draft
1402 // 3 bottom
1403 // positive: photon is outside of material --> Try to enter
1404 // nagative: photon is inside of material --> Try to leave
1405
1406 double T0 = 0;
1407
1408 // The general assumption is: no surface can be hit twice in a row
1409
1410 int cnt = 0;
1411 while (last_surface!=0)
1412 {
1413 cnt ++;
1414
1415 // photon is outside of material --> try to enter
1416 if (last_surface>0)
1417 {
1418 last_surface = EnterGroove( last_surface, n0, p, u);
1419
1420 // successfully entered --> remember time of entrance to calculate transimission
1421 if (last_surface<0)
1422 T0 = p.T();
1423
1424 continue;
1425 }
1426
1427 // photon is inside of material --> try to leave
1428 if (last_surface<0)
1429 {
1430 last_surface = LeavePeak(-last_surface, n0, p, u, T0);
1431
1432 // successfully left --> apply transmission
1433 if (last_surface>=0)
1434 {
1435 if (!Transmission(p.T()-T0, lambda))
1436 throw raytrace_error(kAbsorbed, last_surface, kMaterial,
1437 "TraceRay - Ray absorbed in material");
1438 }
1439
1440 continue;
1441 }
1442 }
1443
1444 // To make this consistent with a mirror system,
1445 // we now change our coordinate system
1446 // Rays from the lens to the camera are up-going (positive sign)
1447 u.fVectorPart.SetZ(-u.Z());
1448
1449 // In the datasheet, it looks as if F is calculated
1450 // towards the center of the lens. It seems things are more
1451 // consistent if the thickness correction in caluating the
1452 // slope angle is omitted and the focal distance is measured
1453 // from the entrance of the lens => FIXME: To be checked
1454 // (Propagating to F means not propagating a distance of F-H from the exit)
1455 //p.fVectorPart.SetZ(fH-fH/2/fN);//fH/2); Found by try-and-error
1456
1457 // We are already at -H, adding F and setting Z=0 means going to -(F+H)
1458 p.fVectorPart.SetZ(0);//fH/2); Found by try-and-error
1459
1460 return cnt>=fMinHits && (fMaxHits==0 || cnt<=fMaxHits) ? cnt : -1;;
1461 }
1462 catch (const raytrace_exception &e)
1463 {
1464 return -e.id();
1465 }
1466
1467/*
1468 try
1469 {
1470 const int cnt = EnterGroove(0, n0, lambda, p, u);
1471
1472 // To make this consistent with a mirror system,
1473 // we now change our coordinate system
1474 // Rays from the lens to the camera are up-going (positive sign)
1475 u.fVectorPart.SetZ(-u.Z());
1476
1477 // In the datasheet, it looks as if F is calculated
1478 // towards the center of the lens
1479 // (Propagating to F means not propagating a distance of F-H/2)
1480 p.fVectorPart.SetZ(0);
1481
1482 return cnt>=fMinHits && (fMaxHits==0 || cnt<=fMaxHits) ? cnt : -1;
1483
1484 }
1485 catch (const int &rc)
1486 {
1487 return rc;
1488 }
1489*/
1490}
1491
1492// Differences:
1493// Does propagate to z=0 at the beginning
1494Int_t MFresnelLens::TraceRay(vector<MQuaternion> &vec, MQuaternion &p, MQuaternion &u, const Short_t &wavelength, bool verbose) const
1495{
1496 // Corsika Coordinates are in cm!
1497
1498 const double lambda = wavelength==0 ? fLambda : wavelength;
1499 if (fAbsorptionLength.GetNp()!=0 &&
1500 (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
1501 {
1502 *fLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
1503 return -1;
1504 }
1505
1506 const double n0 = MFresnelLens::RefractiveIndex(lambda);
1507
1508 // Photon must be at the lens surface
1509 p.PropagateZ(u, 0);
1510 vec.push_back(p);
1511
1512 try
1513 {
1514 int last_surface = EnterGroove(kEntrySurface, n0, p, u);
1515 //cout << "enter1 = " << last_surface << endl;
1516
1517 // last_surface that was hit (photon originates from)
1518 // 0 entrance (Z=0) or exit (Z=-fH) surface
1519 // 1 slope
1520 // 2 draft
1521 // 3 bottom
1522 // positive: photon is outside of material --> Try to enter
1523 // nagative: photon is inside of material --> Try to leave
1524
1525 double T0 = 0;
1526
1527 // The general assumption is: no surface can be hit twice in a row
1528
1529 int cnt = 0;
1530 while (last_surface!=0)
1531 {
1532 cnt ++;
1533 vec.push_back(p);
1534
1535 // photon is outside of material --> try to enter
1536 if (last_surface>0)
1537 {
1538 last_surface = EnterGroove( last_surface, n0, p, u);
1539 //cout << "enter = " << last_surface << endl;
1540
1541 // successfully entered --> remember time of entrance to calculate transimission
1542 if (last_surface<0)
1543 T0 = p.T();
1544
1545 continue;
1546 }
1547
1548 // photon is inside of material --> try to leave
1549 if (last_surface<0)
1550 {
1551 last_surface = LeavePeak(-last_surface, n0, p, u, T0);
1552 //cout << "leave = " << last_surface << endl;
1553
1554 // successfully left --> apply transmission
1555 if (last_surface>=0)
1556 {
1557 if (!Transmission(p.T()-T0, lambda))
1558 throw raytrace_error(kAbsorbed, last_surface, kMaterial,
1559 "TraceRay - Ray absorbed in material");
1560 }
1561
1562 continue;
1563 }
1564 }
1565
1566 vec.push_back(p);
1567 return cnt;
1568 }
1569 catch (const raytrace_exception &e)
1570 {
1571 if (verbose)
1572 *fLog << all << e.id() << ": " << e.what() << endl;
1573
1574 // Hit point at bottom surface beyond allowed range
1575 // FIXME: Only if surface is kExitSurface
1576 if (e.id()==2342)
1577 vec.push_back(p);
1578
1579 return -e.id();
1580 }
1581}
Note: See TracBrowser for help on using the repository browser.