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

Last change on this file since 19674 was 19647, checked in by tbretz, 5 years ago
That was the better fix
File size: 52.1 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 = std::max<size_t>(j==0 ? ix : ix+step, 1);
702
703 for (int i=beg; i!=end; i+=step)
704 {
705 const Groove &groove1 = fGrooves[i-1];
706 const Groove &groove2 = fGrooves[i];
707
708 const double &z1 = groove1.draft.h;
709 const double &z2 = groove2.slope.h;
710
711 const double &r1 = groove1.r;
712 const double &r2 = groove2.r;
713
714 Cone cone;
715 cone.tan_theta = -(r2-r1)/(z2-z1);
716 cone.tan_theta2 = cone.tan_theta*cone.tan_theta;
717 cone.z = z1 + r1/cone.tan_theta;
718
719 const double &Z = cone.z;
720 const double &t2 = cone.tan_theta2;
721
722 const double a = t2 - Ur2;
723 const double b = Ur2*pz - Up2 - Z*t2;
724
725 const double h = Z-pz;
726 const double h2 = h*h;
727
728 // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ]
729
730 const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2;
731 if (radix<0)
732 continue;
733
734 const double sqrt_radix = sqrt(radix);
735
736 const double dz[2] =
737 {
738 (+sqrt_radix - b)/a,
739 (-sqrt_radix - b)/a
740 };
741
742 if (dz[0]>=z2 && dz[0]<=z1)
743 return i;
744
745 if (dz[1]>=z2 && dz[1]<=z1)
746 return i;
747 }
748 }
749
750 return -1;
751}
752
753// --------------------------------------------------------------------------
754//
755// If no transmission was given returns true. Otherwaise calculates the
756// absorption length for a flight time dt in the material and a photon
757// with wavelength lambda. The flight time is converted to a geometrical
758// using the speed of light in the medium.
759//
760// Returns true if the poton passed, false if it was absorbed.
761//
762bool MFresnelLens::Transmission(double dt, double lambda) const
763{
764 if (fAbsorptionLength.GetNp()==0)
765 return true;
766
767 // FIXME: Speed up!
768 const double alpha = fAbsorptionLength.Eval(lambda);
769
770 // We only have the travel time, thus we have to convert back to distance
771 // Note that the transmission coefficients are w.r.t. to geometrical
772 // distance not light-travel distance. Thus the distance has to be corrected
773 // for the corresponding refractive index of the material.
774 const double cm = dt/fVc;
775
776 const double trans = exp(-cm/alpha);
777 return gRandom->Uniform()<trans;
778}
779
780/*
781// surface=0 : incoming ray
782// surface=1 : slope
783// surface=2 : draft
784// surface=3 : bottom
785int MFresnelLens::EnterGroove(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir) const
786{
787 const double rx = pos.R();
788
789 if (surface==3)
790 {
791 //cout << "Bottom as origin invalid" << endl;
792 throw -1;
793
794 }
795 if (rx>=fR)
796 {
797 //cout << "Left the lens radius (enter)" << endl;
798 throw -2;
799 }
800 //if (dir.Z()>0)
801 //{
802 // cout << "Upgoing, outside of the material" << endl;
803 // PropagateZ(pos, dir, dir.Z()>0 ? 3 : -3);
804 // return -1;
805 //}
806
807
808 // Calculate the ordinal number of the groove correpsonding to rx
809 const int ix = TMath::FloorNint(rx/fW);
810
811 // Photons was just injected (test both surfaces) or came from the other surface
812 if (surface==0 || surface==2)
813 {
814 // Get the possible intersection point with the slope angle
815 const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
816
817 // We hit the slope angle
818 if (z1!=0)
819 {
820 // Move photon to new hit position
821 pos.PropagateZ(dir, z1);
822
823 if (fSlopeAbsorption)
824 throw -100;
825
826 // Get the normal vector of the surface which was hit
827 const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
828 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
829
830 // Get the optical transition of the direction vector
831 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
832
833 // Transition was Reflection - try again
834 if (ret==1 || ret==2)
835 return EnterGroove(1, n0, lambda, pos, dir)+1;
836
837 // Transition was Refraction - enter
838 if (ret>=3)
839 return LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
840
841 // Error occured (see ApplyTransition for details)
842 //cout << "ERR[TIR1]" << endl;
843 throw -3;
844 }
845 }
846
847 // Photons was just injected (test both surfaces) or came from the other surface
848 if (surface==0 || surface==1)
849 {
850 const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
851
852 // We hit the draft angle
853 if (z2!=0)
854 {
855 // Move photon to new hit position
856 pos.PropagateZ(dir, z2);
857
858 if (fDraftAbsorption)
859 throw -101;
860
861 // Get the normal vector of the surface which was hit
862 const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
863 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
864
865 // Get the optical transition of the direction vector
866 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
867
868 // Transition was Reflection - try again
869 if (ret==1 || ret==2)
870 return EnterGroove(2, n0, lambda, pos, dir)+1;
871
872 // Transition was Refraction - enter
873 if (ret>=3)
874 return -LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
875
876 // Error occured (see ApplyTransition for details)
877 //cout << "ERR[TIR2]" << endl;
878 throw -4;
879 }
880 }
881
882 if (dir.Z()>0)
883 {
884 //cout << "Upgoing, outside of the material" << endl;
885 //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
886 throw -5;
887 }
888
889 // The ray has left the peak at the bottom(?)
890 //cout << "ERR[N/A]" << endl;
891 throw -6;
892}
893*/
894
895
896// surface=0 : incoming ray
897// surface=1 : slope
898// surface=2 : draft
899// surface=3 : bottom
900int MFresnelLens::EnterGroove(int surface, double n0, MQuaternion &pos, MQuaternion &dir) const
901{
902 const double rx = pos.R();
903
904 if (surface==kExitSurface)
905 throw raytrace_error(kEnter+kInvalidOrigin, surface, -1,
906 "EnterGroove - Bottom as origin invalid");
907
908 if (rx>=fR) // This is an error as the direction vector is now invalid
909 throw raytrace_error(kEnter+kOutsideRadius, surface, -1,
910 "EnterGroove - Surface hit outside allowed radius");
911
912 /*
913 if (dir.Z()>0)
914 return -1;
915 }*/
916
917
918 // FIXME: There is a very tiny chance that a ray hits the same surface twice for
919 // very horizontal rays. Checking this needs to make sure that the same
920 // solution is not just found again.
921
922 // Calculate the ordinal number of the groove correpsonding to rx
923 const int ix = TMath::FloorNint(rx/fW);
924
925 // Photons was just injected (test both surfaces) or came from the other surface
926 if (surface==kEntrySurface || surface==kDraftSurface)
927 {
928 // Get the possible intersection point with the slope angle
929 const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
930
931 // We hit the slope angle
932 if (z1!=0)
933 {
934 // Move photon to new hit position
935 pos.PropagateZ(dir, z1);
936 if (fSlopeAbsorption)
937 throw raytrace_user(kEnter+kAbsorbed, surface, kSlopeSurface,
938 "EnterGroove - Photon absorbed by slope surface");
939
940 // Get the normal vector of the surface which was hit
941 const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
942 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
943
944 // Get the optical transition of the direction vector
945 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
946
947 // Transition was Reflection - try again
948 if (ret==1 || ret==2)
949 return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
950
951 // Transition was Refraction - enter
952 if (ret>=3)
953 return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
954
955 // Error occured (see ApplyTransition for details)
956 throw raytrace_error(kEnter+kTransitionError, surface, kSlopeSurface,
957 "EnterGroove - MOptics::ApplyTransition failed for slope surface");
958 }
959 }
960
961 // Photons was just injected (test both surfaces) or came from the other surface
962 if (surface==kEntrySurface || surface==kSlopeSurface)
963 {
964 const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
965
966 // We hit the draft angle
967 if (z2!=0)
968 {
969 // Move photon to new hit position
970 pos.PropagateZ(dir, z2);
971 if (fDraftAbsorption)
972 throw raytrace_user(kEnter+kAbsorbed, surface, kDraftSurface,
973 "EnterGroove - Photon absorbed by draft surface");
974
975 // Get the normal vector of the surface which was hit
976 const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
977 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
978
979 // Get the optical transition of the direction vector
980 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
981
982 // Transition was Reflection - try again
983 if (ret==1 || ret==2)
984 return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
985
986 // Transition was Refraction - enter
987 if (ret>=3)
988 return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
989
990 // Error occured (see ApplyTransition for details)
991 throw raytrace_error(kEnter+kTransitionError, surface, kDraftSurface,
992 "EnterGroove - MOptics::ApplyTransition failed for draft surface");
993 }
994 }
995
996 if (dir.Z()>0)
997 {
998 // We have missed both surfaces and we are upgoing...
999 // ... ray can be discarded
1000 throw raytrace_info(kEnter+kStrayUpgoing, surface, kNoSurface,
1001 "EnterGroove - Particle is upgoing and has hit no surface");
1002 }
1003
1004 // The ray has left the peak at the bottom(?)
1005 throw raytrace_error(kEnter+kStrayDowngoing, surface, kNoSurface,
1006 "EnterGroove - Particle is downgoing and has hit no surface");
1007}
1008
1009/*
1010// Leave the peak from inside the material, either thought the draft surface or the
1011// slope surface or the bottom connecting the valley of both
1012int MFresnelLens::LeavePeak(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir, double T0) const
1013{
1014 const double rx = pos.R();
1015
1016 if (rx>=fR)
1017 {
1018 //cout << "Left the lens radius (leave)" << endl;
1019 throw -10;
1020 }
1021
1022 if (dir.Z()>0 && surface!=3) // && surface!=4)
1023 {
1024 //cout << "Upgoing, inside of the material" << endl;
1025 //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
1026 throw -11;
1027 }
1028
1029 if (surface!=1 && surface!=2 && surface!=3) // && surface!=4)
1030 {
1031 //cout << "Surface of origin invalid" << endl;
1032 throw -12;
1033 }
1034
1035
1036 // Calculate the ordinal number of the groove correpsonding to rx
1037 const int ix = TMath::FloorNint(rx/fW);
1038
1039 // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
1040
1041 Cone slope = fGrooves[ix].slope;
1042 Cone draft = fGrooves[ix].draft;
1043
1044 const bool is_draft = rx>fGrooves[ix].r;
1045 if (is_draft)
1046 {
1047 // We are in the volume under the draft angle... taking the slope from ix+1
1048 if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
1049 slope = fGrooves[ix+1].slope;
1050 }
1051 else
1052 {
1053 // We are in the volume under the slope angle... taking the draft from ix-1
1054 if (ix>0) // FIXME: Check whether this is correct
1055 draft = fGrooves[ix-1].draft;
1056 }
1057
1058 if (is_draft+1!=surface && (surface==1 || surface==2))
1059 cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
1060
1061 if (surface==3)
1062 {
1063 //cout << "Upgoing, coming from the bottom of the lens" << endl;
1064 // Find out which triangle (peak) the photon is going to enter
1065 // then proceed...
1066 throw -13;
1067 }
1068
1069
1070 // We are inside the material and downgoing, so if we come from a slope surface,
1071 // we can only hit a draft surface after and vice versa
1072 if (is_draft || surface==3)
1073 {
1074 const double z1 = CalcIntersection(pos, dir, slope);
1075
1076 // We hit the slope angle and are currently in the volume under the draft surface
1077 if (z1!=0)
1078 {
1079 // Move photon to new hit position
1080 pos.PropagateZ(dir, z1);
1081
1082 if (fSlopeAbsorption)
1083 throw -200;
1084
1085 // Get the normal vector of the surface which was hit
1086 const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
1087 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1088
1089 // Get the optical transition of the direction vector
1090 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1091
1092 // Transition was Reflection - try again
1093 if (ret==1 || ret==2)
1094 return LeavePeak(1, n0, lambda, pos, dir, T0)+1;
1095
1096 // Transition was Refraction - leave
1097 if (ret>=3)
1098 {
1099 if (!Transmission(pos.T()-T0, lambda))
1100 throw -14;
1101
1102 return EnterGroove(1, n0, lambda, pos, dir)+1;
1103 }
1104
1105 // Error occured (see ApplyTransition for details)
1106 //cout << "ERR[TIR3]" << endl;
1107 throw -15;
1108 }
1109 }
1110
1111 if (!is_draft || surface==3)
1112 {
1113 const double z2 = CalcIntersection(pos, dir, draft);
1114
1115 // We hit the draft angle from the inside and are currently in the volume under the slope angle
1116 if (z2!=0)
1117 {
1118 // Move photon to new hit position
1119 pos.PropagateZ(dir, z2);
1120
1121 if (fDraftAbsorption)
1122 throw -201;
1123
1124 // Get the normal vector of the surface which was hit
1125 const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
1126 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1127
1128 // Get the optical transition of the direction vector
1129 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1130
1131 // Transition was Reflection - try again
1132 if (ret==1 || ret==2)
1133 return LeavePeak(2, n0, lambda, pos, dir, T0)+1;
1134
1135 // Transition was Refraction - leave
1136 if (ret>=3)
1137 {
1138 if (!Transmission(pos.T()-T0, lambda))
1139 throw -16;
1140
1141 return EnterGroove(2, n0, lambda, pos, dir)+1;
1142 }
1143
1144 // Error occured (see ApplyTransition for details)
1145 //cout << "ERR[TIR4]" << endl;
1146 throw -17;
1147 }
1148 }
1149
1150 if (surface==3)// || surface==4)
1151 {
1152 //cout << ix << " Lost bottom reflected ray " << surface << endl;
1153 throw -18;
1154 }
1155
1156 // The ray has left the peak at the bottom
1157
1158 // FIXME: There is a tiny chance to escape to the side
1159 // As there is a slope in the bottom surface of the peak
1160
1161 // Move photon to new hit position
1162 pos.PropagateZ(dir, -fH);
1163
1164 if (pos.R()>fR)
1165 {
1166 //cout << "Left the lens radius (bottom)" << endl;
1167 throw -19;
1168 }
1169
1170 // Get the normal vector of the surface which was hit
1171 const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
1172
1173 // Get the optical transition of the direction vector
1174 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1175
1176 // Transition was Reflection
1177 // (Photon scattered back from the bottom of the lens)
1178 if (ret==1 || ret==2)
1179 return LeavePeak(3, n0, lambda, pos, dir, T0)+1;
1180
1181 // Transition was Refraction
1182 // (Photon left at the bottom of the lens)
1183 if (ret>=3)
1184 {
1185 if (!Transmission(pos.T()-T0, lambda))
1186 throw -20;
1187
1188 return 0;
1189 }
1190
1191 // Error occured (see ApplyTransition for details)
1192 //cout << "ERR[TIR5]" << endl;
1193 throw -21;
1194}*/
1195
1196// Leave the peak from inside the material, either thought the draft surface or the
1197// slope surface or the bottom connecting the valley of both
1198int MFresnelLens::LeavePeak(int surface, double n0, MQuaternion &pos, MQuaternion &dir, double T0) const
1199{
1200 const double rx = pos.R();
1201
1202 if (rx>=fR) // This is an error as the direction vector is now invalid
1203 throw raytrace_error(kLeave+kOutsideRadius, surface, kNoSurface,
1204 "LeavePeak - Surface hit outside allowed radius");
1205
1206 // FIXME: Can we track them further?
1207 if (fDisableMultiEntry && dir.Z()>0 && surface!=3/* && surface!=4*/)
1208 throw raytrace_info(kLeave+kStrayUpgoing, surface, kNoSurface,
1209 "LeavePeak - Particle is upgoing inside the material and does not come from the bottom");
1210
1211 if (surface!=kSlopeSurface && surface!=kDraftSurface && surface!=kExitSurface/* && surface!=4*/)
1212 throw raytrace_error(kLeave+kInvalidOrigin, surface, kNoSurface,
1213 "LeavePeak - Invalid surface of origin");
1214
1215
1216 // Calculate the ordinal number of the groove correpsonding to rx
1217 const int ix = TMath::FloorNint(rx/fW);
1218
1219 // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
1220
1221 Cone slope = fGrooves[ix].slope;
1222 Cone draft = fGrooves[ix].draft;
1223
1224 //if (is_draft+1!=surface && (surface==1 || surface==2))
1225 // cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
1226
1227 const bool is_draft = rx>fGrooves[ix].r;
1228 if (is_draft)
1229 {
1230 // We are in the volume under the draft angle... taking the slope from ix+1
1231 if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
1232 slope = fGrooves[ix+1].slope;
1233 }
1234 else
1235 {
1236 // We are in the volume under the slope angle... taking the draft from ix-1
1237 if (ix>0) // FIXME: Check whether this is correct
1238 draft = fGrooves[ix-1].draft;
1239 }
1240
1241 if (surface==kExitSurface)
1242 {
1243 if (!fBottomReflection)
1244 throw raytrace_user(kLeave+kAbsorbed, surface, kExitSurface,
1245 "LeavePeak - Particle absorbed on the bottom");
1246
1247 const int in = FindPeak(ix, pos, dir);
1248
1249 // This might happen if the ray is very flat and leaving
1250 // the lens before hitting the border boundary of the grooves
1251 if (in<0)
1252 throw raytrace_error(kLeave+kNoSurfaceFound, kExitSurface, kNoSurface,
1253 "LeavePeak - No hit surface found for particle reflected at the bottom");
1254
1255 slope = fGrooves[in].slope;
1256 draft = fGrooves[in==0 ? 0 : in-1].draft;
1257 }
1258
1259 // FIXME: There is a chance that we can hit the same surface twice (for very horizontal rays
1260 // but this requires a proper selection of the hit point
1261
1262 // We are inside the material and downgoing, so if we come from a slope surface,
1263 // we can only hit a draft surface after and vice versa
1264 if (is_draft || surface==kExitSurface)
1265 {
1266 const double z1 = CalcIntersection(pos, dir, slope);
1267
1268 // We hit the slope angle and are currently in the volume under the draft surface
1269 if (z1!=0)
1270 {
1271 // Move photon to new hit position
1272 pos.PropagateZ(dir, z1);
1273
1274 if (fSlopeAbsorption)
1275 throw raytrace_user(kLeave+kAbsorbed, surface, kSlopeSurface,
1276 "LeavePeak - Photon absorbed by slope surface");
1277
1278 // Get the normal vector of the surface which was hit
1279 const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
1280 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1281
1282 // Get the optical transition of the direction vector
1283 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1284
1285 // Transition was Reflection - try again
1286 if (ret==1 || ret==2)
1287 return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, T0)+1;
1288
1289 // Transition was Refraction - leave
1290 if (ret>=3) // Transmission
1291 return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
1292
1293 // Error occured (see ApplyTransition for details)
1294 throw raytrace_error(kLeave+kTransitionError, surface, kSlopeSurface,
1295 "LeavePeak - MOptics::ApplyTransition failed for slope surface");
1296 }
1297 }
1298
1299 if (!is_draft || surface==kExitSurface)
1300 {
1301 const double z2 = CalcIntersection(pos, dir, draft);
1302
1303 // We hit the draft angle from the inside and are currently in the volume under the slope angle
1304 if (z2!=0)
1305 {
1306 // Move photon to new hit position
1307 pos.PropagateZ(dir, z2);
1308
1309 if (fDraftAbsorption)
1310 throw raytrace_user(kLeave+kAbsorbed, surface, kDraftSurface,
1311 "LeavePeak - Photon absorbed by draft surface");
1312
1313 // Get the normal vector of the surface which was hit
1314 const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
1315 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1316
1317 // Get the optical transition of the direction vector
1318 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1319
1320 // Transition was Reflection - try again
1321 if (ret==1 || ret==2)
1322 return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, T0)+1;
1323
1324 // Transition was Refraction - leave
1325 if (ret>=3) // Transmission
1326 return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
1327
1328 // Error occured (see ApplyTransition for details)
1329 //cout << "ERR[TIR4]" << endl;
1330 throw raytrace_error(kLeave+kTransitionError, surface, kDraftSurface,
1331 "LeavePeak - MOptics::ApplyTransition failed for draft surface");
1332 }
1333 }
1334
1335 if (surface==kExitSurface/* || surface==4*/)
1336 throw raytrace_error(kLeave+kFoundSurfaceUnavailable, kExitSurface, is_draft?kSlopeSurface:kDraftSurface,
1337 "LeavePeak - Ray reflected on the bottom did not hit the found surface");
1338
1339 // The ray has left the peak at the bottom
1340
1341 // FIXME: There is a tiny chance to escape to the side
1342 // As there is a slope in the bottom surface of the peak
1343
1344 // FIXME: Theoretically, a ray can hit the same surface twice
1345
1346 // Move photon to new hit position
1347 pos.PropagateZ(dir, -fH);
1348
1349 if (pos.R()>fR)
1350 throw raytrace_info(kLeave+kOutsideRadius, surface, kExitSurface,
1351 "LeavePeak - Hit point at the bottom surface is beyond allowed radius");
1352
1353 // Get the normal vector of the surface which was hit
1354 const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
1355
1356 // Get the optical transition of the direction vector
1357 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1358
1359 // Transition was Reflection
1360 // (Photon scattered back from the bottom of the lens)
1361 if (ret==1 || ret==2)
1362 return -kExitSurface;//LeavePeak(3, n0, lambda, pos, dir, T0)+1;
1363
1364 // Transition was Refraction
1365 // (Photon left at the bottom of the lens)
1366 if (ret>=3) // Transmission
1367 return kPhotonHasLeft;
1368
1369 // Error occured (see ApplyTransition for details)
1370 throw raytrace_error(kLeave+kTransitionError, surface, kExitSurface, "LeavePeak - MOptics::ApplyTransition failed for bottom surface");
1371}
1372
1373
1374// Differences:
1375// Returns a 'reflected' vector at z=0
1376// Does not propagate to z=0 at the beginning
1377Int_t MFresnelLens::ExecuteOptics(MQuaternion &p, MQuaternion &u, const Short_t &wavelength) const
1378{
1379 // Corsika Coordinates are in cm!
1380
1381 const double lambda = wavelength==0 ? fLambda : wavelength;
1382 if (fAbsorptionLength.GetNp()!=0 &&
1383 (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
1384 {
1385 *fLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
1386 return -1;
1387 }
1388
1389 const double n0 = MFresnelLens::RefractiveIndex(lambda);
1390
1391 try
1392 {
1393 int last_surface = EnterGroove(kEntrySurface, n0, p, u);
1394
1395 // last_surface that was hit (photon originates from)
1396 // 0 entrance (Z=0) or exit (Z=-fH) surface
1397 // 1 slope
1398 // 2 draft
1399 // 3 bottom
1400 // positive: photon is outside of material --> Try to enter
1401 // nagative: photon is inside of material --> Try to leave
1402
1403 double T0 = 0;
1404
1405 // The general assumption is: no surface can be hit twice in a row
1406
1407 int cnt = 0;
1408 while (last_surface!=0)
1409 {
1410 cnt ++;
1411
1412 // photon is outside of material --> try to enter
1413 if (last_surface>0)
1414 {
1415 last_surface = EnterGroove( last_surface, n0, p, u);
1416
1417 // successfully entered --> remember time of entrance to calculate transimission
1418 if (last_surface<0)
1419 T0 = p.T();
1420
1421 continue;
1422 }
1423
1424 // photon is inside of material --> try to leave
1425 if (last_surface<0)
1426 {
1427 last_surface = LeavePeak(-last_surface, n0, p, u, T0);
1428
1429 // successfully left --> apply transmission
1430 if (last_surface>=0)
1431 {
1432 if (!Transmission(p.T()-T0, lambda))
1433 throw raytrace_error(kAbsorbed, last_surface, kMaterial,
1434 "TraceRay - Ray absorbed in material");
1435 }
1436
1437 continue;
1438 }
1439 }
1440
1441 // To make this consistent with a mirror system,
1442 // we now change our coordinate system
1443 // Rays from the lens to the camera are up-going (positive sign)
1444 u.fVectorPart.SetZ(-u.Z());
1445
1446 // In the datasheet, it looks as if F is calculated
1447 // towards the center of the lens. It seems things are more
1448 // consistent if the thickness correction in caluating the
1449 // slope angle is omitted and the focal distance is measured
1450 // from the entrance of the lens => FIXME: To be checked
1451 // (Propagating to F means not propagating a distance of F-H from the exit)
1452 //p.fVectorPart.SetZ(fH-fH/2/fN);//fH/2); Found by try-and-error
1453
1454 // We are already at -H, adding F and setting Z=0 means going to -(F+H)
1455 p.fVectorPart.SetZ(0);//fH/2); Found by try-and-error
1456
1457 return cnt>=fMinHits && (fMaxHits==0 || cnt<=fMaxHits) ? cnt : -1;;
1458 }
1459 catch (const raytrace_exception &e)
1460 {
1461 return -e.id();
1462 }
1463
1464/*
1465 try
1466 {
1467 const int cnt = EnterGroove(0, n0, lambda, p, u);
1468
1469 // To make this consistent with a mirror system,
1470 // we now change our coordinate system
1471 // Rays from the lens to the camera are up-going (positive sign)
1472 u.fVectorPart.SetZ(-u.Z());
1473
1474 // In the datasheet, it looks as if F is calculated
1475 // towards the center of the lens
1476 // (Propagating to F means not propagating a distance of F-H/2)
1477 p.fVectorPart.SetZ(0);
1478
1479 return cnt>=fMinHits && (fMaxHits==0 || cnt<=fMaxHits) ? cnt : -1;
1480
1481 }
1482 catch (const int &rc)
1483 {
1484 return rc;
1485 }
1486*/
1487}
1488
1489// Differences:
1490// Does propagate to z=0 at the beginning
1491Int_t MFresnelLens::TraceRay(vector<MQuaternion> &vec, MQuaternion &p, MQuaternion &u, const Short_t &wavelength, bool verbose) const
1492{
1493 // Corsika Coordinates are in cm!
1494
1495 const double lambda = wavelength==0 ? fLambda : wavelength;
1496 if (fAbsorptionLength.GetNp()!=0 &&
1497 (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
1498 {
1499 *fLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
1500 return -1;
1501 }
1502
1503 const double n0 = MFresnelLens::RefractiveIndex(lambda);
1504
1505 // Photon must be at the lens surface
1506 p.PropagateZ(u, 0);
1507 vec.push_back(p);
1508
1509 try
1510 {
1511 int last_surface = EnterGroove(kEntrySurface, n0, p, u);
1512 //cout << "enter1 = " << last_surface << endl;
1513
1514 // last_surface that was hit (photon originates from)
1515 // 0 entrance (Z=0) or exit (Z=-fH) surface
1516 // 1 slope
1517 // 2 draft
1518 // 3 bottom
1519 // positive: photon is outside of material --> Try to enter
1520 // nagative: photon is inside of material --> Try to leave
1521
1522 double T0 = 0;
1523
1524 // The general assumption is: no surface can be hit twice in a row
1525
1526 int cnt = 0;
1527 while (last_surface!=0)
1528 {
1529 cnt ++;
1530 vec.push_back(p);
1531
1532 // photon is outside of material --> try to enter
1533 if (last_surface>0)
1534 {
1535 last_surface = EnterGroove( last_surface, n0, p, u);
1536 //cout << "enter = " << last_surface << endl;
1537
1538 // successfully entered --> remember time of entrance to calculate transimission
1539 if (last_surface<0)
1540 T0 = p.T();
1541
1542 continue;
1543 }
1544
1545 // photon is inside of material --> try to leave
1546 if (last_surface<0)
1547 {
1548 last_surface = LeavePeak(-last_surface, n0, p, u, T0);
1549 //cout << "leave = " << last_surface << endl;
1550
1551 // successfully left --> apply transmission
1552 if (last_surface>=0)
1553 {
1554 if (!Transmission(p.T()-T0, lambda))
1555 throw raytrace_error(kAbsorbed, last_surface, kMaterial,
1556 "TraceRay - Ray absorbed in material");
1557 }
1558
1559 continue;
1560 }
1561 }
1562
1563 vec.push_back(p);
1564 return cnt;
1565 }
1566 catch (const raytrace_exception &e)
1567 {
1568 if (verbose)
1569 *fLog << all << e.id() << ": " << e.what() << endl;
1570
1571 // Hit point at bottom surface beyond allowed range
1572 // FIXME: Only if surface is kExitSurface
1573 if (e.id()==2342)
1574 vec.push_back(p);
1575
1576 return -e.id();
1577 }
1578}
Note: See TracBrowser for help on using the repository browser.