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

Last change on this file since 20091 was 19935, checked in by tbretz, 5 years ago
Updated refractive index of air for 546nm in Lens definition and added a reference for it
File size: 53.3 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 (uint32_t 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];
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
326Int_t MFresnelLens::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
327{
328 Bool_t rc = kFALSE;
329
330 if (IsEnvDefined(env, prefix, "SurfaceRoughness", print))
331 {
332 rc = kTRUE;
333 if (!GetEnvValue(env, prefix, "SurfaceRoughness", fPSF))
334 return kERROR;
335 }
336
337 const int correction = GetEnvValue(env, prefix, "Transmission.FresnelCorrection", -1);
338 const float thickness = GetEnvValue(env, prefix, "Transmission.Thickness", -1.0); // [cm]
339 const TString fname = GetEnvValue(env, prefix, "Transmission.FileName", "");
340
341 const bool correction_valid = correction>=0;
342 const bool thickness_valid = thickness>0;
343 const bool fname_valid = !fname.IsNull();
344
345 if (!correction_valid && !thickness_valid && !fname_valid)
346 return rc;
347
348 if (correction_valid && thickness_valid && fname_valid)
349 return ReadTransmission(fname, thickness, correction) >= 0 || rc;
350
351 *fLog << err << "Reading transmission file required FileName, Thickness and FresnelCorrection." << endl;
352 return kERROR;
353}
354
355// ==========================================================================
356
357// --------------------------------------------------------------------------
358//
359// Refractive Index of PMMA, according to
360// https://refractiveindex.info/?shelf=organic&book=poly(methyl_methacrylate)&page=Szczurowski
361//
362// 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}
363//
364// Returns the refractive index n as a function of wavelength (in nanometers)
365//
366double MFresnelLens::RefractiveIndex(double lambda)
367{
368 const double l2 = lambda*lambda;
369
370 const double c0 = 0.99654/(1-0.00787e6/l2);
371 const double c1 = 0.18964/(1-0.02191e6/l2);
372 const double c2 = 0.00411/(1-3.85727e6/l2);
373
374 return sqrt(1+c0+c1+c2);
375}
376
377// --------------------------------------------------------------------------
378//
379// A Fresnel lens with parabolic surface calculated with the sagittag
380// function (k=-1) and a correction for the thickness of the lens
381// on the curvature. See also PhD thesis, Tim Niggemann ch. 7.1.1.
382//
383// see also W.J.Smith, Modern Optical Engineering, 2.8 The "Thin Lens"
384// 1/f = (n-1)/radius Eq. 2.36 with thickness t = 0
385// bfl = f Eq. 2.37 and R2 = inf (c2 = 0)
386//
387// Parameters are:
388// The distance from the center r
389// The focal length to be achieved F
390// The refractive index of the outer medium (usually air) n0
391// The refractive index of the lens material (e.g. PMMA) n1
392// The thichness of the lens d
393//
394// r, F and d have to be in the same units.
395//
396// Return the slope angle alpha [rad]. The Slope angle is defined with
397// respect to the plane of the lens. (0 at the center, decreasing
398// with increasing radial distance)
399//
400double MFresnelLens::SlopeAngleParabolic(double r, double F, double n0, double n1, double d)
401{
402 // In the datasheet, it looks as if F is calculated
403 // towards the center of the lens. It seems things are more
404 // consistent if the thickness correction in caluating the
405 // slope angle is omitted and the focal distance is measured
406 // from the entrance of the lens => FIXME: To be checked
407 const double rn = n1/n0;
408 const double c = (rn - 1) * (F + d/rn); // FIXME: try and error with a large d
409 return -atan(r/c);
410
411 // F = 50.21
412 // d= 10 d=20
413 // -: 47 43.7
414 // 0: 53.5 57.0
415 // +: 60.3 70.3
416}
417
418// --------------------------------------------------------------------------
419//
420// A Fresnel lens with an optimized parabolic surface calculated with
421// the sagittag function (k=-1) and fitted coefficients according
422// to Master thesis, Eichler.
423//
424// Note that for this setup other parameters must be fixed
425//
426// Parameters are:
427// The distance from the center r
428//
429// r is in cm.
430//
431// Return the slope angle alpha [rad]. The Slope angle is defined with
432// respect to the plane of the lens. (0 at the center, decreasing
433// with increasing radial distance)
434//
435double MFresnelLens::SlopeAngleAspherical(double r)
436{
437 // Master, Eichler [r/cm]
438 return -atan( r/26.47
439 +2*1.18e-4 * 1e1*r
440 +4*1.34e-9 * 1e3*r*r*r
441 +6*9.52e-15 * 1e5*r*r*r*r*r
442 -8*2.04e-19 * 1e7*r*r*r*r*r*r*r);
443}
444
445// --------------------------------------------------------------------------
446//
447// Ideal angle of the Fresnel surfaces at a distance r from the center
448// to achieve a focal distance F for a positive Fresnel lens made
449// from a material with a refractive index n.
450// A positive Fresnel lens is one which focuses light from infinity
451// (the side with the grooves) to a point (the flat side of the lens).
452//
453// The calculation follows
454// https://shodhganga.inflibnet.ac.in/bitstream/10603/131007/13/09_chapter%202.pdf
455// Here, a thin lens is assumed
456//
457// sin(omega) = r / sqrt(r^2+F^2)
458// tan(alpha) = sin(omega) / [ 1 - sqrt(n^2-sin(omega)^2) ]
459//
460// Return alpha [rad] as a function of the radial distance r, the
461// focal length F and the refractive index n. r and F have to have
462// the same units. The Slope angle is defined with respect to the plane
463// of the lens. (0 at the center, decreasing with increasing radial
464// distance)
465//
466double MFresnelLens::SlopeAngleOptimized(double r, double F, double n)
467{
468 // Use F+d/2
469 double so = r / sqrt(r*r + F*F);
470 return atan(so / (1-sqrt(n*n - so*so))); // alpha<0, Range [0deg; -50deg]
471}
472
473// --------------------------------------------------------------------------
474//
475// Currently calles SlopeAngleParabolic(r, F, 1, n, d)
476// Refractive Index air: https://refractiveindex.info/?shelf=other&book=air&page=Ciddor
477//
478double MFresnelLens::SlopeAngle(double r, double F, double n, double d)
479{
480 //return SlopeAngleAspherical(r);
481 return SlopeAngleParabolic(r, F, 1.000278, n, d);
482}
483
484
485//
486// Draft angle of the Orafol SC943 According to the thesis of Eichler
487// and NiggemannTim Niggemann:
488//
489// The surface of the lens follows the shape of a parabolic lens to compensate spherical aberration
490// Draft angle: psi(r) = 3deg + r * 0.0473deg/mm
491//
492// The draft angle is returned in radians and is defined w.r.t. to the
493// normal of the lens surface. (almost 90deg at the center,
494// decreasing with increasing radial distance)
495//
496double MFresnelLens::DraftAngle(double r)
497{
498 return (3 + r*0.473)*TMath::DegToRad(); // Range [0deg; 15deg]
499}
500
501// ==========================================================================
502
503// --------------------------------------------------------------------------
504//
505// Return the total Area of all mirrors. Note, that it is recalculated
506// with any call.
507//
508Double_t MFresnelLens::GetA() const
509{
510 return fMaxR*fMaxR*TMath::Pi();
511}
512
513// --------------------------------------------------------------------------
514//
515// Check with a rough estimate whether a photon can hit the reflector.
516//
517Bool_t MFresnelLens::CanHit(const MQuaternion &p) const
518{
519 // p is given in the reflectory coordinate frame. This is meant as a
520 // fast check without lengthy calculations to omit all photons which
521 // cannot hit the reflector at all
522 return p.R2()<fMaxR*fMaxR;
523}
524
525// ==========================================================================
526
527// FIXME: The rays could be 'reflected' inside the material
528// (even though its going out) or vice versa
529static double RandomTheta(double psf)
530{
531 return psf>0 ? MMath::RndmPSF(psf)/2 : 0;
532}
533
534// FIXME: The rays could be 'reflected' inside the material
535// (even though its going out) or vice versa
536static double RandomPhi(double r, double psf)
537{
538 return psf>0 ? MMath::RndmPSF(psf)/2 : 0;
539}
540
541
542// --------------------------------------------------------------------------
543//
544// Calculate the intersection point beweteen a line defined by the position p
545// and the direction u and a cone defined by the object cone.
546//
547// Z: position of peak of cone
548// theta: opening angle of cone
549//
550// Distance r of cone surface at given z from z-axis
551// r_cone(z) = (Z-z)*tan(theta)
552//
553// Equalition of line
554// (x) (p.x) (u.x/u.z)
555// (y) = (p.y) + dz * (u.y/u.z)
556// (z) (p.z) ( 1 )
557//
558// Normalization
559// U.x := u.x/u.z
560// U.y := u.y/u.z
561//
562// Distance of line at given z from z-axis
563// 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
564//
565// Equation to be solved
566// r_cone(z) = r_line(z)
567//
568// Solved with wxmaxima:
569//
570// [0] solve((px+(z-pz)*Ux)^2+(py+(z-pz)*Uy)^2= ((Z-z)*t)^2, z);
571//
572// 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),
573// 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)
574//
575double MFresnelLens::CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Cone &cone) const
576{
577 const double &Z = cone.z;
578
579 const double Ux = u.X()/u.Z();
580 const double Uy = u.Y()/u.Z();
581
582 const double px = p.X();
583 const double py = p.Y();
584 const double pz = p.Z();
585
586 //const double &t = cone.tan_theta;
587 const double &t2 = cone.tan_theta2;
588
589 const double Ur2 = Ux*Ux + Uy*Uy;
590 const double pr2 = px*px + py*py;
591 const double Up2 = Ux*px + Uy*py;
592
593 const double cr2 = Ux*py - Uy*px;
594
595 const double a = t2 - Ur2;
596 const double b = Ur2*pz - Up2 - Z*t2;
597
598 const double h = Z-pz;
599 const double h2 = h*h;
600
601 // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ]
602
603 const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2;
604 if (radix<0)
605 return 0;
606
607 const double sqrt_radix = sqrt(radix);
608
609 const double dz[2] =
610 {
611 (+sqrt_radix - b)/a,
612 (-sqrt_radix - b)/a
613 };
614
615 // Return the closest solution inside the allowed range
616 // which is in the direction of movement
617
618 const double &H = cone.h;
619
620 const bool is_inside0 = dz[0]>=H && dz[0]<0;
621 const bool is_inside1 = dz[1]>=H && dz[1]<0;
622
623 // FIXME: Simplify!
624 if (!is_inside0 && !is_inside1)
625 return 0;
626
627 // Only dz[0] is in the right z-range
628 if (is_inside0 && !is_inside1)
629 {
630 // Check if dz[0] is in the right direction
631 if ((u.Z()>=0 && dz[0]>=p.Z()) ||
632 (u.Z()< 0 && dz[0]< p.Z()))
633 return dz[0];
634
635 return 0;
636 }
637
638 // Only dz[1] is in the right z-range
639 if (!is_inside0 && is_inside1)
640 {
641 // Check if dz[1] is in the right direction
642 if ((u.Z()>=0 && dz[1]>=p.Z()) ||
643 (u.Z()< 0 && dz[1]< p.Z()))
644 return dz[1];
645
646 return 0;
647 }
648
649 /*
650 if (is_inside0^is_inside1)
651 {
652 if (u.Z()>=0)
653 return dz[0]>p.Z() ? dz[0] : dz[1];
654 else
655 return dz[0]<p.Z() ? dz[0] : dz[1];
656 }*/
657
658
659 // dz[0] and dz[1] are in the right range
660 // return the surface which is hit first
661
662 // moving upwards
663 if (u.Z()>=0)
664 {
665 // Both solution could be correct
666 if (dz[0]>=p.Z() && dz[1]>=p.Z())
667 return std::min(dz[0], dz[1]);
668
669 // only one solution can be correct
670 return dz[0]>=p.Z() ? dz[0] : dz[1];
671 }
672 else
673 {
674 // Both solution could be correct
675 if (dz[0]<p.Z() && dz[1]<p.Z())
676 return std::max(dz[0], dz[1]);
677
678 // only one solution can be correct
679 return dz[0]<p.Z() ? dz[0] : dz[1];
680 }
681}
682
683// --------------------------------------------------------------------------
684//
685// Find the peak (draft+slope) which will be hit by the photon which
686// is defined by position p and direction u. ix gives the index of the groove
687// to originate the search from.
688//
689// Returns the index of the groove to which the surface belongs, -1 if no
690// matching surface was found.
691//
692int MFresnelLens::FindPeak(size_t ix, const MQuaternion &p, const MQuaternion &u) const
693{
694 // ---------------------------
695 // check for first groove first
696 if (ix==0)
697 {
698 const auto test = p.fVectorPart + (fGrooves[0].slope.h-p.Z())/u.Z()*u.fVectorPart;
699 if (test.XYvector().Mod()<fGrooves[0].r)
700 return 0;
701 }
702
703 // r = sqrt( (px + t*ux) + (py + t*uy)^2 )
704 // dr/dt = (2*uy*(dz*uy+py)+2*ux*(dz*ux+px))/(2*sqrt((dz*uy+py)^2+(dz*ux+px)^2))
705 // dr/dt = (uy*py + ux*px)/sqrt(py^2+px^2)
706 const bool outgoing = u.X()*p.X() + u.Y()*p.Y() > 0; // r is (at least locally) increasing
707
708 // ---------------------------
709 const double Ux = u.X()/u.Z();
710 const double Uy = u.Y()/u.Z();
711
712 const double px = p.X();
713 const double py = p.Y();
714 const double pz = p.Z();
715
716 const double Ur2 = Ux*Ux + Uy*Uy;
717 const double cr2 = Ux*py - Uy*px;
718 const double pr2 = px*px + py*py;
719 const double Up2 = Ux*px + Uy*py;
720
721 //for (int i=1; i<fGrooves.size(); i++)
722
723 // To speed up the search, search first along the radial moving direction of
724 // the photon. If that was not successfull, try in the opposite direction.
725 // FIXME: This could still fail in some very rare cases, for some extremely flat trajectories
726 for (int j=0; j<2; j++)
727 {
728 const bool first = j==0;
729
730 const int step = outgoing ^ !first ? 1 : -1;
731 const int end = outgoing ^ !first ? fGrooves.size() : 1;
732 const int beg = std::max<size_t>(j==0 ? ix : ix+step, 1);
733
734 for (int i=beg; i!=end; i+=step)
735 {
736 const Groove &groove1 = fGrooves[i-1];
737 const Groove &groove2 = fGrooves[i];
738
739 const double &z1 = groove1.draft.h;
740 const double &z2 = groove2.slope.h;
741
742 const double &r1 = groove1.r;
743 const double &r2 = groove2.r;
744
745 Cone cone;
746 cone.tan_theta = -(r2-r1)/(z2-z1);
747 cone.tan_theta2 = cone.tan_theta*cone.tan_theta;
748 cone.z = z1 + r1/cone.tan_theta;
749
750 const double &Z = cone.z;
751 const double &t2 = cone.tan_theta2;
752
753 const double a = t2 - Ur2;
754 const double b = Ur2*pz - Up2 - Z*t2;
755
756 const double h = Z-pz;
757 const double h2 = h*h;
758
759 // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ]
760
761 const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2;
762 if (radix<0)
763 continue;
764
765 const double sqrt_radix = sqrt(radix);
766
767 const double dz[2] =
768 {
769 (+sqrt_radix - b)/a,
770 (-sqrt_radix - b)/a
771 };
772
773 if (dz[0]>=z2 && dz[0]<=z1)
774 return i;
775
776 if (dz[1]>=z2 && dz[1]<=z1)
777 return i;
778 }
779 }
780
781 return -1;
782}
783
784// --------------------------------------------------------------------------
785//
786// If no transmission was given returns true. Otherwaise calculates the
787// absorption length for a flight time dt in the material and a photon
788// with wavelength lambda. The flight time is converted to a geometrical
789// using the speed of light in the medium.
790//
791// Returns true if the poton passed, false if it was absorbed.
792//
793bool MFresnelLens::Transmission(double dt, double lambda) const
794{
795 if (fAbsorptionLength.GetNp()==0)
796 return true;
797
798 // FIXME: Speed up!
799 const double alpha = fAbsorptionLength.Eval(lambda);
800
801 // We only have the travel time, thus we have to convert back to distance
802 // Note that the transmission coefficients are w.r.t. to geometrical
803 // distance not light-travel distance. Thus the distance has to be corrected
804 // for the corresponding refractive index of the material.
805 const double cm = dt/fVc;
806
807 const double trans = exp(-cm/alpha);
808 return gRandom->Uniform()<trans;
809}
810
811/*
812// surface=0 : incoming ray
813// surface=1 : slope
814// surface=2 : draft
815// surface=3 : bottom
816int MFresnelLens::EnterGroove(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir) const
817{
818 const double rx = pos.R();
819
820 if (surface==3)
821 {
822 //cout << "Bottom as origin invalid" << endl;
823 throw -1;
824
825 }
826 if (rx>=fR)
827 {
828 //cout << "Left the lens radius (enter)" << endl;
829 throw -2;
830 }
831 //if (dir.Z()>0)
832 //{
833 // cout << "Upgoing, outside of the material" << endl;
834 // PropagateZ(pos, dir, dir.Z()>0 ? 3 : -3);
835 // return -1;
836 //}
837
838
839 // Calculate the ordinal number of the groove correpsonding to rx
840 const int ix = TMath::FloorNint(rx/fW);
841
842 // Photons was just injected (test both surfaces) or came from the other surface
843 if (surface==0 || surface==2)
844 {
845 // Get the possible intersection point with the slope angle
846 const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
847
848 // We hit the slope angle
849 if (z1!=0)
850 {
851 // Move photon to new hit position
852 pos.PropagateZ(dir, z1);
853
854 if (fSlopeAbsorption)
855 throw -100;
856
857 // Get the normal vector of the surface which was hit
858 const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
859 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
860
861 // Get the optical transition of the direction vector
862 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
863
864 // Transition was Reflection - try again
865 if (ret==1 || ret==2)
866 return EnterGroove(1, n0, lambda, pos, dir)+1;
867
868 // Transition was Refraction - enter
869 if (ret>=3)
870 return LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
871
872 // Error occured (see ApplyTransition for details)
873 //cout << "ERR[TIR1]" << endl;
874 throw -3;
875 }
876 }
877
878 // Photons was just injected (test both surfaces) or came from the other surface
879 if (surface==0 || surface==1)
880 {
881 const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
882
883 // We hit the draft angle
884 if (z2!=0)
885 {
886 // Move photon to new hit position
887 pos.PropagateZ(dir, z2);
888
889 if (fDraftAbsorption)
890 throw -101;
891
892 // Get the normal vector of the surface which was hit
893 const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
894 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
895
896 // Get the optical transition of the direction vector
897 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
898
899 // Transition was Reflection - try again
900 if (ret==1 || ret==2)
901 return EnterGroove(2, n0, lambda, pos, dir)+1;
902
903 // Transition was Refraction - enter
904 if (ret>=3)
905 return -LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
906
907 // Error occured (see ApplyTransition for details)
908 //cout << "ERR[TIR2]" << endl;
909 throw -4;
910 }
911 }
912
913 if (dir.Z()>0)
914 {
915 //cout << "Upgoing, outside of the material" << endl;
916 //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
917 throw -5;
918 }
919
920 // The ray has left the peak at the bottom(?)
921 //cout << "ERR[N/A]" << endl;
922 throw -6;
923}
924*/
925
926
927// surface=0 : incoming ray
928// surface=1 : slope
929// surface=2 : draft
930// surface=3 : bottom
931int MFresnelLens::EnterGroove(int surface, double n0, MQuaternion &pos, MQuaternion &dir) const
932{
933 const double rx = pos.R();
934
935 if (surface==kExitSurface)
936 throw raytrace_error(kEnter+kInvalidOrigin, surface, -1,
937 "EnterGroove - Bottom as origin invalid");
938
939 if (rx>=fR) // This is an error as the direction vector is now invalid
940 throw raytrace_error(kEnter+kOutsideRadius, surface, -1,
941 "EnterGroove - Surface hit outside allowed radius");
942
943 /*
944 if (dir.Z()>0)
945 return -1;
946 }*/
947
948
949 // FIXME: There is a very tiny chance that a ray hits the same surface twice for
950 // very horizontal rays. Checking this needs to make sure that the same
951 // solution is not just found again.
952
953 // Calculate the ordinal number of the groove correpsonding to rx
954 const int ix = TMath::FloorNint(rx/fW);
955
956 // Photons was just injected (test both surfaces) or came from the other surface
957 if (surface==kEntrySurface || surface==kDraftSurface)
958 {
959 // Get the possible intersection point with the slope angle
960 const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
961
962 // We hit the slope angle
963 if (z1!=0)
964 {
965 // Move photon to new hit position
966 pos.PropagateZ(dir, z1);
967 if (fSlopeAbsorption)
968 throw raytrace_user(kEnter+kAbsorbed, surface, kSlopeSurface,
969 "EnterGroove - Photon absorbed by slope surface");
970
971 // Get the normal vector of the surface which was hit
972 const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
973 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
974
975 // Get the optical transition of the direction vector
976 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
977
978 // Transition was Reflection - try again
979 if (ret==1 || ret==2)
980 return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
981
982 // Transition was Refraction - enter
983 if (ret>=3)
984 return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
985
986 // Error occured (see ApplyTransition for details)
987 throw raytrace_error(kEnter+kTransitionError, surface, kSlopeSurface,
988 "EnterGroove - MOptics::ApplyTransition failed for slope surface");
989 }
990 }
991
992 // Photons was just injected (test both surfaces) or came from the other surface
993 if (surface==kEntrySurface || surface==kSlopeSurface)
994 {
995 const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
996
997 // We hit the draft angle
998 if (z2!=0)
999 {
1000 // Move photon to new hit position
1001 pos.PropagateZ(dir, z2);
1002 if (fDraftAbsorption)
1003 throw raytrace_user(kEnter+kAbsorbed, surface, kDraftSurface,
1004 "EnterGroove - Photon absorbed by draft surface");
1005
1006 // Get the normal vector of the surface which was hit
1007 const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
1008 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1009
1010 // Get the optical transition of the direction vector
1011 const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
1012
1013 // Transition was Reflection - try again
1014 if (ret==1 || ret==2)
1015 return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
1016
1017 // Transition was Refraction - enter
1018 if (ret>=3)
1019 return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
1020
1021 // Error occured (see ApplyTransition for details)
1022 throw raytrace_error(kEnter+kTransitionError, surface, kDraftSurface,
1023 "EnterGroove - MOptics::ApplyTransition failed for draft surface");
1024 }
1025 }
1026
1027 if (dir.Z()>0)
1028 {
1029 // We have missed both surfaces and we are upgoing...
1030 // ... ray can be discarded
1031 throw raytrace_info(kEnter+kStrayUpgoing, surface, kNoSurface,
1032 "EnterGroove - Particle is upgoing and has hit no surface");
1033 }
1034
1035 // The ray has left the peak at the bottom(?)
1036 throw raytrace_error(kEnter+kStrayDowngoing, surface, kNoSurface,
1037 "EnterGroove - Particle is downgoing and has hit no surface");
1038}
1039
1040/*
1041// Leave the peak from inside the material, either thought the draft surface or the
1042// slope surface or the bottom connecting the valley of both
1043int MFresnelLens::LeavePeak(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir, double T0) const
1044{
1045 const double rx = pos.R();
1046
1047 if (rx>=fR)
1048 {
1049 //cout << "Left the lens radius (leave)" << endl;
1050 throw -10;
1051 }
1052
1053 if (dir.Z()>0 && surface!=3) // && surface!=4)
1054 {
1055 //cout << "Upgoing, inside of the material" << endl;
1056 //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
1057 throw -11;
1058 }
1059
1060 if (surface!=1 && surface!=2 && surface!=3) // && surface!=4)
1061 {
1062 //cout << "Surface of origin invalid" << endl;
1063 throw -12;
1064 }
1065
1066
1067 // Calculate the ordinal number of the groove correpsonding to rx
1068 const int ix = TMath::FloorNint(rx/fW);
1069
1070 // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
1071
1072 Cone slope = fGrooves[ix].slope;
1073 Cone draft = fGrooves[ix].draft;
1074
1075 const bool is_draft = rx>fGrooves[ix].r;
1076 if (is_draft)
1077 {
1078 // We are in the volume under the draft angle... taking the slope from ix+1
1079 if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
1080 slope = fGrooves[ix+1].slope;
1081 }
1082 else
1083 {
1084 // We are in the volume under the slope angle... taking the draft from ix-1
1085 if (ix>0) // FIXME: Check whether this is correct
1086 draft = fGrooves[ix-1].draft;
1087 }
1088
1089 if (is_draft+1!=surface && (surface==1 || surface==2))
1090 cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
1091
1092 if (surface==3)
1093 {
1094 //cout << "Upgoing, coming from the bottom of the lens" << endl;
1095 // Find out which triangle (peak) the photon is going to enter
1096 // then proceed...
1097 throw -13;
1098 }
1099
1100
1101 // We are inside the material and downgoing, so if we come from a slope surface,
1102 // we can only hit a draft surface after and vice versa
1103 if (is_draft || surface==3)
1104 {
1105 const double z1 = CalcIntersection(pos, dir, slope);
1106
1107 // We hit the slope angle and are currently in the volume under the draft surface
1108 if (z1!=0)
1109 {
1110 // Move photon to new hit position
1111 pos.PropagateZ(dir, z1);
1112
1113 if (fSlopeAbsorption)
1114 throw -200;
1115
1116 // Get the normal vector of the surface which was hit
1117 const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
1118 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1119
1120 // Get the optical transition of the direction vector
1121 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1122
1123 // Transition was Reflection - try again
1124 if (ret==1 || ret==2)
1125 return LeavePeak(1, n0, lambda, pos, dir, T0)+1;
1126
1127 // Transition was Refraction - leave
1128 if (ret>=3)
1129 {
1130 if (!Transmission(pos.T()-T0, lambda))
1131 throw -14;
1132
1133 return EnterGroove(1, n0, lambda, pos, dir)+1;
1134 }
1135
1136 // Error occured (see ApplyTransition for details)
1137 //cout << "ERR[TIR3]" << endl;
1138 throw -15;
1139 }
1140 }
1141
1142 if (!is_draft || surface==3)
1143 {
1144 const double z2 = CalcIntersection(pos, dir, draft);
1145
1146 // We hit the draft angle from the inside and are currently in the volume under the slope angle
1147 if (z2!=0)
1148 {
1149 // Move photon to new hit position
1150 pos.PropagateZ(dir, z2);
1151
1152 if (fDraftAbsorption)
1153 throw -201;
1154
1155 // Get the normal vector of the surface which was hit
1156 const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
1157 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1158
1159 // Get the optical transition of the direction vector
1160 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1161
1162 // Transition was Reflection - try again
1163 if (ret==1 || ret==2)
1164 return LeavePeak(2, n0, lambda, pos, dir, T0)+1;
1165
1166 // Transition was Refraction - leave
1167 if (ret>=3)
1168 {
1169 if (!Transmission(pos.T()-T0, lambda))
1170 throw -16;
1171
1172 return EnterGroove(2, n0, lambda, pos, dir)+1;
1173 }
1174
1175 // Error occured (see ApplyTransition for details)
1176 //cout << "ERR[TIR4]" << endl;
1177 throw -17;
1178 }
1179 }
1180
1181 if (surface==3)// || surface==4)
1182 {
1183 //cout << ix << " Lost bottom reflected ray " << surface << endl;
1184 throw -18;
1185 }
1186
1187 // The ray has left the peak at the bottom
1188
1189 // FIXME: There is a tiny chance to escape to the side
1190 // As there is a slope in the bottom surface of the peak
1191
1192 // Move photon to new hit position
1193 pos.PropagateZ(dir, -fH);
1194
1195 if (pos.R()>fR)
1196 {
1197 //cout << "Left the lens radius (bottom)" << endl;
1198 throw -19;
1199 }
1200
1201 // Get the normal vector of the surface which was hit
1202 const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
1203
1204 // Get the optical transition of the direction vector
1205 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
1206
1207 // Transition was Reflection
1208 // (Photon scattered back from the bottom of the lens)
1209 if (ret==1 || ret==2)
1210 return LeavePeak(3, n0, lambda, pos, dir, T0)+1;
1211
1212 // Transition was Refraction
1213 // (Photon left at the bottom of the lens)
1214 if (ret>=3)
1215 {
1216 if (!Transmission(pos.T()-T0, lambda))
1217 throw -20;
1218
1219 return 0;
1220 }
1221
1222 // Error occured (see ApplyTransition for details)
1223 //cout << "ERR[TIR5]" << endl;
1224 throw -21;
1225}*/
1226
1227// Leave the peak from inside the material, either thought the draft surface or the
1228// slope surface or the bottom connecting the valley of both
1229int MFresnelLens::LeavePeak(int surface, double n0, MQuaternion &pos, MQuaternion &dir, double T0) const
1230{
1231 const double rx = pos.R();
1232
1233 if (rx>=fR) // This is an error as the direction vector is now invalid
1234 throw raytrace_error(kLeave+kOutsideRadius, surface, kNoSurface,
1235 "LeavePeak - Surface hit outside allowed radius");
1236
1237 // FIXME: Can we track them further?
1238 if (fDisableMultiEntry && dir.Z()>0 && surface!=3/* && surface!=4*/)
1239 throw raytrace_info(kLeave+kStrayUpgoing, surface, kNoSurface,
1240 "LeavePeak - Particle is upgoing inside the material and does not come from the bottom");
1241
1242 if (surface!=kSlopeSurface && surface!=kDraftSurface && surface!=kExitSurface/* && surface!=4*/)
1243 throw raytrace_error(kLeave+kInvalidOrigin, surface, kNoSurface,
1244 "LeavePeak - Invalid surface of origin");
1245
1246
1247 // Calculate the ordinal number of the groove correpsonding to rx
1248 const uint32_t ix = TMath::FloorNint(rx/fW);
1249
1250 // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
1251
1252 Cone slope = fGrooves[ix].slope;
1253 Cone draft = fGrooves[ix].draft;
1254
1255 //if (is_draft+1!=surface && (surface==1 || surface==2))
1256 // cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
1257
1258 const bool is_draft = rx>fGrooves[ix].r;
1259 if (is_draft)
1260 {
1261 // We are in the volume under the draft angle... taking the slope from ix+1
1262 if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
1263 slope = fGrooves[ix+1].slope;
1264 }
1265 else
1266 {
1267 // We are in the volume under the slope angle... taking the draft from ix-1
1268 if (ix>0) // FIXME: Check whether this is correct
1269 draft = fGrooves[ix-1].draft;
1270 }
1271
1272 if (surface==kExitSurface)
1273 {
1274 if (!fBottomReflection)
1275 throw raytrace_user(kLeave+kAbsorbed, surface, kExitSurface,
1276 "LeavePeak - Particle absorbed on the bottom");
1277
1278 const int in = FindPeak(ix, pos, dir);
1279
1280 // This might happen if the ray is very flat and leaving
1281 // the lens before hitting the border boundary of the grooves
1282 if (in<0)
1283 throw raytrace_error(kLeave+kNoSurfaceFound, kExitSurface, kNoSurface,
1284 "LeavePeak - No hit surface found for particle reflected at the bottom");
1285
1286 slope = fGrooves[in].slope;
1287 draft = fGrooves[in==0 ? 0 : in-1].draft;
1288 }
1289
1290 // FIXME: There is a chance that we can hit the same surface twice (for very horizontal rays
1291 // but this requires a proper selection of the hit point
1292
1293 // We are inside the material and downgoing, so if we come from a slope surface,
1294 // we can only hit a draft surface after and vice versa
1295 if (is_draft || surface==kExitSurface)
1296 {
1297 const double z1 = CalcIntersection(pos, dir, slope);
1298
1299 // We hit the slope angle and are currently in the volume under the draft surface
1300 if (z1!=0)
1301 {
1302 // Move photon to new hit position
1303 pos.PropagateZ(dir, z1);
1304
1305 if (fSlopeAbsorption)
1306 throw raytrace_user(kLeave+kAbsorbed, surface, kSlopeSurface,
1307 "LeavePeak - Photon absorbed by slope surface");
1308
1309 // Get the normal vector of the surface which was hit
1310 const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
1311 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1312
1313 // Get the optical transition of the direction vector
1314 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1315
1316 // Transition was Reflection - try again
1317 if (ret==1 || ret==2)
1318 return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, T0)+1;
1319
1320 // Transition was Refraction - leave
1321 if (ret>=3) // Transmission
1322 return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
1323
1324 // Error occured (see ApplyTransition for details)
1325 throw raytrace_error(kLeave+kTransitionError, surface, kSlopeSurface,
1326 "LeavePeak - MOptics::ApplyTransition failed for slope surface");
1327 }
1328 }
1329
1330 if (!is_draft || surface==kExitSurface)
1331 {
1332 const double z2 = CalcIntersection(pos, dir, draft);
1333
1334 // We hit the draft angle from the inside and are currently in the volume under the slope angle
1335 if (z2!=0)
1336 {
1337 // Move photon to new hit position
1338 pos.PropagateZ(dir, z2);
1339
1340 if (fDraftAbsorption)
1341 throw raytrace_user(kLeave+kAbsorbed, surface, kDraftSurface,
1342 "LeavePeak - Photon absorbed by draft surface");
1343
1344 // Get the normal vector of the surface which was hit
1345 const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
1346 pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
1347
1348 // Get the optical transition of the direction vector
1349 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1350
1351 // Transition was Reflection - try again
1352 if (ret==1 || ret==2)
1353 return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, T0)+1;
1354
1355 // Transition was Refraction - leave
1356 if (ret>=3) // Transmission
1357 return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
1358
1359 // Error occured (see ApplyTransition for details)
1360 //cout << "ERR[TIR4]" << endl;
1361 throw raytrace_error(kLeave+kTransitionError, surface, kDraftSurface,
1362 "LeavePeak - MOptics::ApplyTransition failed for draft surface");
1363 }
1364 }
1365
1366 if (surface==kExitSurface/* || surface==4*/)
1367 throw raytrace_error(kLeave+kFoundSurfaceUnavailable, kExitSurface, is_draft?kSlopeSurface:kDraftSurface,
1368 "LeavePeak - Ray reflected on the bottom did not hit the found surface");
1369
1370 // The ray has left the peak at the bottom
1371
1372 // FIXME: There is a tiny chance to escape to the side
1373 // As there is a slope in the bottom surface of the peak
1374
1375 // FIXME: Theoretically, a ray can hit the same surface twice
1376
1377 // Move photon to new hit position
1378 pos.PropagateZ(dir, -fH);
1379
1380 if (pos.R()>fR)
1381 throw raytrace_info(kLeave+kOutsideRadius, surface, kExitSurface,
1382 "LeavePeak - Hit point at the bottom surface is beyond allowed radius");
1383
1384 // Get the normal vector of the surface which was hit
1385 const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
1386
1387 // Get the optical transition of the direction vector
1388 const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
1389
1390 // Transition was Reflection
1391 // (Photon scattered back from the bottom of the lens)
1392 if (ret==1 || ret==2)
1393 return -kExitSurface;//LeavePeak(3, n0, lambda, pos, dir, T0)+1;
1394
1395 // Transition was Refraction
1396 // (Photon left at the bottom of the lens)
1397 if (ret>=3) // Transmission
1398 return kPhotonHasLeft;
1399
1400 // Error occured (see ApplyTransition for details)
1401 throw raytrace_error(kLeave+kTransitionError, surface, kExitSurface, "LeavePeak - MOptics::ApplyTransition failed for bottom surface");
1402}
1403
1404
1405// Differences:
1406// Returns a 'reflected' vector at z=0
1407// Does not propagate to z=0 at the beginning
1408Int_t MFresnelLens::ExecuteOptics(MQuaternion &p, MQuaternion &u, const Short_t &wavelength) const
1409{
1410 // Corsika Coordinates are in cm!
1411
1412 const double lambda = wavelength==0 ? fLambda : wavelength;
1413 if (fAbsorptionLength.GetNp()!=0 &&
1414 (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
1415 {
1416 *fLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
1417 return -1;
1418 }
1419
1420 const double n0 = MFresnelLens::RefractiveIndex(lambda);
1421
1422 try
1423 {
1424 int last_surface = kEntrySurface;//EnterGroove(kEntrySurface, n0, p, u);
1425
1426 // last_surface that was hit (photon originates from)
1427 // 0 entrance (Z=0) or exit (Z=-fH) surface
1428 // 1 slope
1429 // 2 draft
1430 // 3 bottom
1431 // positive: photon is outside of material --> Try to enter
1432 // nagative: photon is inside of material --> Try to leave
1433
1434 double T0 = 0;//last_surface<0 ? p.T() : 0;
1435
1436 // The general assumption is: no surface can be hit twice in a row
1437
1438 int cnt = -1;
1439 while (last_surface!=0)
1440 {
1441 cnt ++;
1442
1443 // photon is outside of material --> try to enter
1444 if (last_surface>0)
1445 {
1446 last_surface = EnterGroove(last_surface, n0, p, u);
1447
1448 // successfully entered --> remember time of entrance to calculate transimission
1449 if (last_surface<0)
1450 T0 = p.T();
1451
1452 continue;
1453 }
1454
1455 // photon is inside of material --> try to leave
1456 if (last_surface<0)
1457 {
1458 last_surface = LeavePeak(-last_surface, n0, p, u, T0);
1459
1460 // successfully left --> apply transmission
1461 if (last_surface>=0)
1462 {
1463 if (!Transmission(p.T()-T0, lambda))
1464 throw raytrace_error(kAbsorbed, last_surface, kMaterial,
1465 "TraceRay - Ray absorbed in material");
1466 }
1467
1468 continue;
1469 }
1470 }
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. It seems things are more
1479 // consistent if the thickness correction in caluating the
1480 // slope angle is omitted and the focal distance is measured
1481 // from the entrance of the lens => FIXME: To be checked
1482 // (Propagating to F means not propagating a distance of F-H from the exit)
1483 //p.fVectorPart.SetZ(fH-fH/2/fN);//fH/2); Found by try-and-error
1484
1485 // We are already at -H, adding F and setting Z=0 means going to -(F+H)
1486 p.fVectorPart.SetZ(0);//fH/2); Found by try-and-error
1487
1488 return uint32_t(cnt)>=fMinHits && (fMaxHits==0 || uint32_t(cnt)<=fMaxHits) ? cnt : -1;;
1489 }
1490 catch (const raytrace_exception &e)
1491 {
1492 return -e.id();
1493 }
1494
1495/*
1496 try
1497 {
1498 const int cnt = EnterGroove(0, n0, lambda, p, u);
1499
1500 // To make this consistent with a mirror system,
1501 // we now change our coordinate system
1502 // Rays from the lens to the camera are up-going (positive sign)
1503 u.fVectorPart.SetZ(-u.Z());
1504
1505 // In the datasheet, it looks as if F is calculated
1506 // towards the center of the lens
1507 // (Propagating to F means not propagating a distance of F-H/2)
1508 p.fVectorPart.SetZ(0);
1509
1510 return cnt>=fMinHits && (fMaxHits==0 || cnt<=fMaxHits) ? cnt : -1;
1511
1512 }
1513 catch (const int &rc)
1514 {
1515 return rc;
1516 }
1517*/
1518}
1519
1520// Differences:
1521// Does propagate to z=0 at the beginning
1522Int_t MFresnelLens::TraceRay(vector<MQuaternion> &vec, MQuaternion &p, MQuaternion &u, const Short_t &wavelength, bool verbose) const
1523{
1524 // Corsika Coordinates are in cm!
1525
1526 const double lambda = wavelength==0 ? fLambda : wavelength;
1527 if (fAbsorptionLength.GetNp()!=0 &&
1528 (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
1529 {
1530 *fLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
1531 return -1;
1532 }
1533
1534 const double n0 = MFresnelLens::RefractiveIndex(lambda);
1535
1536 // Photon must be at the lens surface
1537 p.PropagateZ(u, 0);
1538 vec.push_back(p);
1539
1540 try
1541 {
1542 int last_surface = kEntrySurface;//EnterGroove(kEntrySurface, n0, p, u);
1543
1544 // last_surface that was hit (photon originates from)
1545 // 0 entrance (Z=0) or exit (Z=-fH) surface
1546 // 1 slope
1547 // 2 draft
1548 // 3 bottom
1549 // positive: photon is outside of material --> Try to enter
1550 // nagative: photon is inside of material --> Try to leave
1551
1552 double T0 = 0;
1553
1554 // The general assumption is: no surface can be hit twice in a row
1555
1556 int cnt = -1;
1557 while (last_surface!=0)
1558 {
1559 cnt ++;
1560 vec.push_back(p);
1561
1562 // photon is outside of material --> try to enter
1563 if (last_surface>0)
1564 {
1565 last_surface = EnterGroove( last_surface, n0, p, u);
1566 //cout << "enter = " << last_surface << endl;
1567
1568 // successfully entered --> remember time of entrance to calculate transimission
1569 if (last_surface<0)
1570 T0 = p.T();
1571
1572 continue;
1573 }
1574
1575 // photon is inside of material --> try to leave
1576 if (last_surface<0)
1577 {
1578 last_surface = LeavePeak(-last_surface, n0, p, u, T0);
1579 //cout << "leave = " << last_surface << endl;
1580
1581 // successfully left --> apply transmission
1582 if (last_surface>=0)
1583 {
1584 if (!Transmission(p.T()-T0, lambda))
1585 throw raytrace_error(kAbsorbed, last_surface, kMaterial,
1586 "TraceRay - Ray absorbed in material");
1587 }
1588
1589 continue;
1590 }
1591 }
1592
1593 vec.push_back(p);
1594 return cnt;
1595 }
1596 catch (const raytrace_exception &e)
1597 {
1598 if (verbose)
1599 *fLog << all << e.id() << ": " << e.what() << endl;
1600
1601 // Hit point at bottom surface beyond allowed range
1602 // FIXME: Only if surface is kExitSurface
1603 if (e.id()==2342)
1604 vec.push_back(p);
1605
1606 return -e.id();
1607 }
1608}
Note: See TracBrowser for help on using the repository browser.