Changeset 19638


Ignore:
Timestamp:
09/15/19 14:22:38 (5 years ago)
Author:
tbretz
Message:
Updated the algorithm and added comments (still not final, I think)
Location:
trunk/Mars/msimreflector
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Mars/msimreflector/MFresnelLens.cc

    r19632 r19638  
    2727//  MFresnelLens
    2828//
    29 //  For some details on definitions please refer to
    30 //  https://application.wiley-vch.de/berlin/journals/op/07-04/OP0704_S52_S55.pdf
     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
    3137//
    3238//////////////////////////////////////////////////////////////////////////////
     
    4147#include "MReflection.h"
    4248
     49#include "MMath.h"
     50
    4351#include "MLog.h"
    4452#include "MLogManip.h"
     
    4856using namespace std;
    4957
     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
    50146// --------------------------------------------------------------------------
    51147//
    52148// Default constructor
    53149//
    54 MFresnelLens::MFresnelLens(const char *name, const char *title)
     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)
    55154{
    56155    fName  = name  ? name  : "MFresnelLens";
    57156    fTitle = title ? title : "Parameter container storing a collection of several mirrors (reflector)";
    58157
    59     fMaxR = 55/2.;
    60 }
     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            gLog << 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            gLog << 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                gLog << 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// ==========================================================================
    61327
    62328// --------------------------------------------------------------------------
     
    78344
    79345    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// Parameters are:
     355// The distance from the center r
     356// The focal length to be achieved F
     357// The refractive index of the outer medium (usually air) n0
     358// The refractive index of the lens material (e.g. PMMA) n1
     359// The thichness of the lens d
     360//
     361// r, F and d have to be in the same units.
     362//
     363// Return the slope angle alpha [rad]. The Slope angle is defined with
     364// respect to the plane of the lens. (0 at the center, decreasing
     365// with increasing radial distance)
     366//
     367double MFresnelLens::SlopeAngleParabolic(double r, double F, double n0, double n1, double d)
     368{
     369    // PhD, Tim Niggemann, ch 7.1.1
     370    const double rn = n1/n0;
     371    const double c = (rn - 1) * (F + d/rn);
     372    return -atan(r/c);
     373}
     374
     375// --------------------------------------------------------------------------
     376//
     377// A Fresnel lens with an optimized parabolic surface calculated with
     378// the sagittag function (k=-1) and fitted coefficients according
     379// to Master thesis, Eichler.
     380//
     381// Note that for this setup other parameters must be fixed
     382//
     383// Parameters are:
     384// The distance from the center r
     385//
     386// r is in cm.
     387//
     388// Return the slope angle alpha [rad]. The Slope angle is defined with
     389// respect to the plane of the lens. (0 at the center, decreasing
     390// with increasing radial distance)
     391//
     392double MFresnelLens::SlopeAngleAspherical(double r)
     393{
     394    // Master, Eichler [r/cm]
     395    return -atan( r/26.47
     396                 +2*1.18e-4  * 1e1*r
     397                 +4*1.34e-9  * 1e3*r*r*r
     398                 +6*9.52e-15 * 1e5*r*r*r*r*r
     399                 -8*2.04e-19 * 1e7*r*r*r*r*r*r*r);
    80400}
    81401
     
    92412// Here, a thin lens is assumed
    93413//
    94 // The HAWC's Eye lens is an Orafol SC943
    95 // https://www.orafol.com/en/europe/products/optic-solutions/productlines#pl1
    96 //
    97414// sin(omega) = r / sqrt(r^2+F^2)
    98415// tan(alpha) = sin(omega) / [ 1 - sqrt(n^2-sin(omega)^2) ]
     
    104421// distance)
    105422//
    106 double MFresnelLens::SlopeAngle(double r, double F, double n)
    107 {
     423double MFresnelLens::SlopeAngleOptimized(double r, double F, double n)
     424{
     425    // Use F+d/2
    108426    double so = r / sqrt(r*r + F*F);
    109427    return atan(so / (1-sqrt(n*n - so*so))); // alpha<0, Range [0deg; -50deg]
     428}
     429
     430// --------------------------------------------------------------------------
     431//
     432// Currently calles SlopeAngleParabolic(r, F, 1, n, d)
     433//
     434double MFresnelLens::SlopeAngle(double r, double F, double n, double d)
     435{
     436    return SlopeAngleParabolic(r, F, 1.0003, n, d);
    110437}
    111438
     
    126453    return (3 + r*0.473)*TMath::DegToRad(); // Range [0deg; 15deg]
    127454}
     455
     456// ==========================================================================
    128457
    129458// --------------------------------------------------------------------------
     
    149478}
    150479
    151 struct Groove
    152 {
    153     double fTheta;
    154     double fPeakZ;
    155 
    156     double fTanTheta;
    157     double fTanTheta2[3];
    158 
    159     Groove() { }
    160     Groove(double theta, double z) : fTheta(theta), fPeakZ(z)
    161     {
    162         fTanTheta = tan(theta);
    163 
    164         fTanTheta2[0] = fTanTheta*fTanTheta;
    165         fTanTheta2[1] = fTanTheta*fTanTheta * fPeakZ;
    166         fTanTheta2[2] = fTanTheta*fTanTheta * fPeakZ*fPeakZ;
    167     }
    168 };
    169 
    170 double CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Groove &g, const double &fThickness)
    171 {
    172 /*
    173     Z: position of peak
    174 
    175     r_cone(z) = (Z-z)*tan(theta)
    176 
    177     (x)   (p.x)          (u.x)
    178     (y) = (p.y)  + dz *  (u.y)
    179     (z)   (p.z)          (u.z)
    180 
    181     r_line(z) = sqrt(x^2 + y^2) = sqrt( (p.x+dz*u.x)^2 + (p.y+dz*u.y)^2)
    182 
    183     Solved with wmmaxima:
    184 
    185     solve((H-z)^2*t^2 = (px+(z-pz)/uz*ux)^2+(py+(z-pz)/uz*uy)^2, z);
    186 
    187      [
    188       z=-(uz*sqrt((py^2+px^2)*t^2*uz^2+((2*H*py-2*py*pz)*t^2*uy+(2*H*px-2*px*pz)*t^2*ux)*uz+((pz^2-2*H*pz+H^2)*t^2-px^2)*uy^2+2*px*py*ux*uy+((pz^2-2*H*pz+H^2)*t^2-py^2)*ux^2)-H*t^2*uz^2+(-py*uy-px*ux)*uz+pz*uy^2+pz*ux^2)/(t^2*uz^2-uy^2-ux^2)
    189       z= (uz*sqrt((py^2+px^2)*t^2*uz^2+((2*H*py-2*py*pz)*t^2*uy+(2*H*px-2*px*pz)*t^2*ux)*uz+((pz^2-2*H*pz+H^2)*t^2-px^2)*uy^2+2*px*py*ux*uy+((pz^2-2*H*pz+H^2)*t^2-py^2)*ux^2)+H*t^2*uz^2+( py*uy+px*ux)*uz-pz*uy^2-pz*ux^2)/(t^2*uz^2-uy^2-ux^2)
    190      ]
    191 */
     480// ==========================================================================
     481
     482// FIXME: The rays could be 'reflected' inside the material
     483// (even though its going out) or vice versa
     484static double RandomTheta(double psf)
     485{
     486    return psf>0 ? MMath::RndmPSF(psf)/2 : 0;
     487}
     488
     489// FIXME: The rays could be 'reflected' inside the material
     490// (even though its going out) or vice versa
     491static double RandomPhi(double r, double psf)
     492{
     493    return psf>0 ? MMath::RndmPSF(psf)/2 : 0;
     494}
     495
     496
     497// --------------------------------------------------------------------------
     498//
     499// Calculate the intersection point beweteen a line defined by the position p
     500// and the direction u and a cone defined by the object cone.
     501//
     502// Z: position of peak of cone
     503// theta: opening angle of cone
     504//
     505// Distance r of cone surface at given z from z-axis
     506// r_cone(z) = (Z-z)*tan(theta)
     507//
     508// Equalition of line
     509//  (x)   (p.x)          (u.x/u.z)
     510//  (y) = (p.y)  + dz *  (u.y/u.z)
     511//  (z)   (p.z)          (   1   )
     512//
     513// Normalization
     514//  U.x := u.x/u.z
     515//  U.y := u.y/u.z
     516//
     517// Distance of line at given z from z-axis
     518//  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
     519//
     520// Equation to be solved
     521//  r_cone(z) = r_line(z)
     522//
     523// Solved with wxmaxima:
     524//
     525//  [0] solve((px+(z-pz)*Ux)^2+(py+(z-pz)*Uy)^2= ((Z-z)*t)^2, z);
     526//
     527//  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),
     528//  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)
     529//
     530double MFresnelLens::CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Cone &cone) const
     531{
     532    const double &Z = cone.z;
    192533
    193534    const double Ux = u.X()/u.Z();
     
    198539    const double pz = p.Z();
    199540
    200     const double H   = g.fPeakZ;
    201 
    202     const double t   = g.fTanTheta;
    203     const double t2  = t*t;
     541    //const double &t  = cone.tan_theta;
     542    const double &t2 = cone.tan_theta2;
    204543
    205544    const double Ur2 = Ux*Ux + Uy*Uy;
     
    209548    const double cr2 = Ux*py - Uy*px;
    210549
    211     const double a   = t2 - Ur2;
    212     const double b   = Ur2*pz - Up2 - H*t2;
    213 
    214     const double h   = H-pz;
    215     const double h2  = h*h;
     550    const double a = t2 - Ur2;
     551    const double b = Ur2*pz - Up2 - Z*t2;
     552
     553    const double h  = Z-pz;
     554    const double h2 = h*h;
    216555
    217556    // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ]
    218557
    219558    const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2;
    220 
    221559    if (radix<0)
    222560        return 0;
    223561
    224     double dz[2] =
    225     {
    226         (-b+sqrt(radix))/a,
    227         (-b-sqrt(radix))/a
     562    const double sqrt_radix = sqrt(radix);
     563
     564    const double dz[2] =
     565    {
     566        (+sqrt_radix - b)/a,
     567        (-sqrt_radix - b)/a
    228568    };
    229569
    230      if (dz[0]<0 && dz[0]>fThickness)
    231         return dz[0];
    232 
    233     if (dz[1]<0 && dz[1]>fThickness)
    234         return dz[1];
    235 
    236     return 0;
     570    // Return the closest solution inside the allowed range
     571    // which is in the direction of movement
     572
     573    const double &H = cone.h;
     574
     575    const bool is_inside0 = dz[0]>=H && dz[0]<0;
     576    const bool is_inside1 = dz[1]>=H && dz[1]<0;
     577
     578    // FIXME: Simplify!
     579    if (!is_inside0 && !is_inside1)
     580        return 0;
     581
     582    // Only dz[0] is in the right z-range
     583    if (is_inside0 && !is_inside1)
     584    {
     585        // Check if dz[0] is in the right direction
     586        if ((u.Z()>=0 && dz[0]>=p.Z()) ||
     587            (u.Z()< 0 && dz[0]< p.Z()))
     588            return dz[0];
     589
     590        return 0;
     591    }
     592
     593    // Only dz[1] is in the right z-range
     594    if (!is_inside0 && is_inside1)
     595    {
     596        // Check if dz[1] is in the right direction
     597        if ((u.Z()>=0 && dz[1]>=p.Z()) ||
     598            (u.Z()< 0 && dz[1]< p.Z()))
     599            return dz[1];
     600
     601        return 0;
     602    }
     603
     604    /*
     605    if (is_inside0^is_inside1)
     606    {
     607        if (u.Z()>=0)
     608            return dz[0]>p.Z() ? dz[0] : dz[1];
     609        else
     610            return dz[0]<p.Z() ? dz[0] : dz[1];
     611    }*/
     612
     613
     614    // dz[0] and dz[1] are in the right range
     615    // return the surface which is hit first
     616
     617    // moving upwards
     618    if (u.Z()>=0)
     619    {
     620        // Both solution could be correct
     621        if (dz[0]>=p.Z() && dz[1]>=p.Z())
     622            return std::min(dz[0], dz[1]);
     623
     624        // only one solution can be correct
     625        return dz[0]>=p.Z() ? dz[0] : dz[1];
     626    }
     627    else
     628    {
     629        // Both solution could be correct
     630        if (dz[0]<p.Z() && dz[1]<p.Z())
     631            return std::max(dz[0], dz[1]);
     632
     633        // only one solution can be correct
     634        return dz[0]<p.Z() ? dz[0] : dz[1];
     635    }
     636}
     637
     638// --------------------------------------------------------------------------
     639//
     640// Find the peak (draft+slope) which will be hit by the photon which
     641// is defined by position p and direction u. ix gives the index of the groove
     642// to originate the search from.
     643//
     644// Returns the index of the groove to which the surface belongs, -1 if no
     645// matching surface was found.
     646//
     647int MFresnelLens::FindPeak(size_t ix, const MQuaternion &p, const MQuaternion &u) const
     648{
     649    // ---------------------------
     650    // check for first groove first
     651    if (ix==0)
     652    {
     653        const auto test = p.fVectorPart + (fGrooves[0].slope.h-p.Z())/u.Z()*u.fVectorPart;
     654        if (test.XYvector().Mod()<fGrooves[0].r)
     655            return 0;
     656    }
     657
     658    // r = sqrt( (px + t*ux) + (py + t*uy)^2 )
     659    // dr/dt = (2*uy*(dz*uy+py)+2*ux*(dz*ux+px))/(2*sqrt((dz*uy+py)^2+(dz*ux+px)^2))
     660    // dr/dt = (uy*py + ux*px)/sqrt(py^2+px^2)
     661    const bool outgoing = u.X()*p.X() + u.Y()*p.Y() > 0; // r is (at least locally) increasing
     662
     663    // ---------------------------
     664    const double Ux = u.X()/u.Z();
     665    const double Uy = u.Y()/u.Z();
     666
     667    const double px = p.X();
     668    const double py = p.Y();
     669    const double pz = p.Z();
     670
     671    const double Ur2 = Ux*Ux + Uy*Uy;
     672    const double cr2 = Ux*py - Uy*px;
     673    const double pr2 = px*px + py*py;
     674    const double Up2 = Ux*px + Uy*py;
     675
     676    //for (int i=1; i<fGrooves.size(); i++)
     677
     678    // To speed up the search, search first along the radial moving direction of
     679    // the photon. If that was not successfull, try in the opposite direction.
     680    // FIXME: This could still fail in some very rare cases, for some extremely flat trajectories
     681    for (int j=0; j<2; j++)
     682    {
     683        const bool first = j==0;
     684
     685        const int step = outgoing ^ !first ? 1 : -1;
     686        const int end  = outgoing ^ !first ? fGrooves.size() : 1;
     687        const int beg  = j==0 ? ix : ix+(step);
     688
     689        for (int i=beg; i!=end; i+=step)
     690        {
     691            const Groove &groove1 = fGrooves[i-1];
     692            const Groove &groove2 = fGrooves[i];
     693
     694            const double &z1 = groove1.draft.h;
     695            const double &z2 = groove2.slope.h;
     696
     697            const double &r1 = groove1.r;
     698            const double &r2 = groove2.r;
     699
     700            Cone cone;
     701            cone.tan_theta  = -(r2-r1)/(z2-z1);
     702            cone.tan_theta2 = cone.tan_theta*cone.tan_theta;
     703            cone.z          = z1 + r1/cone.tan_theta;
     704
     705            const double &Z  = cone.z;
     706            const double &t2 = cone.tan_theta2;
     707
     708            const double a = t2 - Ur2;
     709            const double b = Ur2*pz - Up2 - Z*t2;
     710
     711            const double h  = Z-pz;
     712            const double h2 = h*h;
     713
     714            // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ]
     715
     716            const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2;
     717            if (radix<0)
     718                continue;
     719
     720            const double sqrt_radix = sqrt(radix);
     721
     722            const double dz[2] =
     723            {
     724                (+sqrt_radix - b)/a,
     725                (-sqrt_radix - b)/a
     726            };
     727
     728            if (dz[0]>=z2 && dz[0]<=z1)
     729                return i;
     730
     731            if (dz[1]>=z2 && dz[1]<=z1)
     732                return i;
     733        }
     734    }
     735
     736    return -1;
     737}
     738
     739// --------------------------------------------------------------------------
     740//
     741// If no transmission was given returns true. Otherwaise calculates the
     742// absorption length for a flight time dt in the material and a photon
     743// with wavelength lambda. The flight time is converted to a geometrical
     744// using the speed of light in the medium.
     745//
     746// Returns true if the poton passed, false if it was absorbed.
     747//
     748bool MFresnelLens::Transmission(double dt, double lambda) const
     749{
     750    if (fAbsorptionLength.GetNp()==0)
     751        return true;
     752
     753    // FIXME: Speed up!
     754    const double alpha = fAbsorptionLength.Eval(lambda);
     755
     756    // We only have the travel time, thus we have to convert back to distance
     757    // Note that the transmission coefficients are w.r.t. to geometrical
     758    // distance not light-travel distance. Thus the distance has to be corrected
     759    // for the corresponding refractive index of the material.
     760    const double cm = dt/fVc; 
     761
     762    const double trans = exp(-cm/alpha);
     763    return gRandom->Uniform()<trans;
    237764}
    238765
    239766/*
    240 double CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Groove &g, const double &fThickness)
    241 {
    242    // p is the position  in the plane  of the lens (px, py, 0,  t)
    243    // u is the direction of the photon             (ux, uy, dz, dt)
    244 
    245    // Assuming a cone with peak at z and opening angle theta
    246 
    247    // Radius at distance z+dz from the tip and at distance r from the axis of the cone
    248    // r = (z+dz*u.z) * tan(theta)
    249 
    250    // Defining
    251    // zc := z+dz
    252 
    253    // Surface of the cone
    254    // (X)         ( tan(theta) * sin(phi) )
    255    // (Y)  = zc * ( tan(theta) * cos(phi) )
    256    // (Z)         (          1            )
    257 
    258    // Line
    259    // (X)        (u.x)   (p.x)
    260    // (Y) = dz * (u.y) + (p.y)
    261    // (Z)        (u.z)   (p.z)
    262 
    263    // Equality
    264    //
    265    // X^2+Y^2 = r^2
    266    //
    267    // (dz*u.x+p.x)^2 + (dz*u.y+p.y)^2 = (z+dz)^2 * tan(theta)^2
    268    //
    269    // dz^2*ux^2 + px^2 + 2*dz*ux*px + dz^2*uy^2 + py^2 + 2*dz*uy*py = (z^2*tan(theta)^2 + dz^2*tan(theta)^2 + 2*z*dz*tan(theta)^2
    270    //
    271    // dz^2*(ux^2 + uy^2 - tan(theta)^2)  +  2*dz*(ux*px + uy*py - z*tan(theta)^2)  +  (px^2+py^2 - z^2*tan(theta)^2)  =  0
    272 
    273    // t   := tan(theta)
    274    // a   := ux^2    + uy^2    -     t^2
    275    // b/2 := ux*px   + uy*py   - z  *t^2 := B
    276    // c   :=    px^2 +    py^2 - z^2*t^2
    277 
    278    // dz = [ -b +- sqrt(b^2 - 4*a*c) ] / [2*a]
    279    // dz = [ -B +- sqrt(B^2 -   a*c) ] /    a
    280 
    281     const double a = u.R2()                    - g.fTanTheta2[0];
    282     const double B = u.XYvector()*p.XYvector() - g.fTanTheta2[1];
    283     const double c = p.R2()                    - g.fTanTheta2[2];
    284 
    285     const double B2 = B*B;
    286     const double ac = a*c;
    287 
    288     if (B2<ac)
     767// surface=0 : incoming ray
     768// surface=1 : slope
     769// surface=2 : draft
     770// surface=3 : bottom
     771int MFresnelLens::EnterGroove(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir) const
     772{
     773    const double rx = pos.R();
     774
     775    if (surface==3)
     776    {
     777        //cout << "Bottom as origin invalid" << endl;
     778        throw -1;
     779
     780    }
     781    if (rx>=fR)
     782    {
     783        //cout << "Left the lens radius (enter)" << endl;
     784        throw -2;
     785    }
     786    //if (dir.Z()>0)
     787    //{
     788    //    cout << "Upgoing, outside of the material" << endl;
     789    //    PropagateZ(pos, dir, dir.Z()>0 ? 3 : -3);
     790    //    return -1;
     791    //}
     792
     793
     794   // Calculate the ordinal number of the groove correpsonding to rx
     795    const int ix = TMath::FloorNint(rx/fW);
     796
     797    // Photons was just injected (test both surfaces) or came from the other surface
     798    if (surface==0 || surface==2)
     799    {
     800        // Get the possible intersection point with the slope angle
     801        const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
     802
     803        // We hit the slope angle
     804        if (z1!=0)
     805        {
     806            // Move photon to new hit position
     807            pos.PropagateZ(dir, z1);
     808
     809            if (fSlopeAbsorption)
     810                throw -100;
     811
     812            // Get the normal vector of the surface which was hit
     813            const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
     814                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     815
     816            // Get the optical transition of the direction vector
     817            const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
     818
     819            // Transition was Reflection - try again
     820            if (ret==1 || ret==2)
     821                return EnterGroove(1, n0, lambda, pos, dir)+1;
     822
     823            // Transition was Refraction - enter
     824            if (ret>=3)
     825                return LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
     826
     827            // Error occured (see ApplyTransition for details)
     828            //cout << "ERR[TIR1]" << endl;
     829            throw -3;
     830        }
     831    }
     832
     833    // Photons was just injected (test both surfaces) or came from the other surface
     834    if (surface==0 || surface==1)
     835    {
     836        const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
     837
     838        // We hit the draft angle
     839        if (z2!=0)
     840        {
     841            // Move photon to new hit position
     842            pos.PropagateZ(dir, z2);
     843
     844            if (fDraftAbsorption)
     845                throw -101;
     846
     847            // Get the normal vector of the surface which was hit
     848            const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
     849                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     850
     851            // Get the optical transition of the direction vector
     852            const int ret = MOptics::ApplyTransition(dir, norm, 1, n0);
     853
     854            // Transition was Reflection - try again
     855            if (ret==1 || ret==2)
     856                return EnterGroove(2, n0, lambda, pos, dir)+1;
     857
     858            // Transition was Refraction - enter
     859            if (ret>=3)
     860                return -LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
     861
     862            // Error occured (see ApplyTransition for details)
     863            //cout << "ERR[TIR2]" << endl;
     864            throw -4;
     865        }
     866    }
     867
     868    if (dir.Z()>0)
     869    {
     870        //cout << "Upgoing, outside of the material" << endl;
     871        //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
     872        throw -5;
     873    }
     874
     875    // The ray has left the peak at the bottom(?)
     876    //cout << "ERR[N/A]" << endl;
     877    throw -6;
     878}
     879*/
     880
     881
     882// surface=0 : incoming ray
     883// surface=1 : slope
     884// surface=2 : draft
     885// surface=3 : bottom
     886int MFresnelLens::EnterGroove(int surface, double n0, MQuaternion &pos, MQuaternion &dir) const
     887{
     888    const double rx = pos.R();
     889
     890    if (surface==kExitSurface)
     891        throw raytrace_error(kEnter+kInvalidOrigin, surface, -1,
     892                             "EnterGroove - Bottom as origin invalid");
     893
     894    if (rx>=fR) // This is an error as the direction vector is now invalid
     895        throw raytrace_error(kEnter+kOutsideRadius, surface, -1,
     896                            "EnterGroove - Surface hit outside allowed radius");
     897
     898    /*
     899    if (dir.Z()>0)
     900        return -1;
     901    }*/
     902
     903
     904    // FIXME: There is a very tiny chance that a ray hits the same surface twice for
     905    // very horizontal rays. Checking this needs to make sure that the same
     906    // solution is not just found again.
     907
     908    // Calculate the ordinal number of the groove correpsonding to rx
     909    const int ix = TMath::FloorNint(rx/fW);
     910
     911    // Photons was just injected (test both surfaces) or came from the other surface
     912    if (surface==kEntrySurface || surface==kDraftSurface)
     913    {
     914        // Get the possible intersection point with the slope angle
     915        const double z1 = CalcIntersection(pos, dir, fGrooves[ix].slope);
     916
     917        // We hit the slope angle
     918        if (z1!=0)
     919        {
     920            // Move photon to new hit position
     921            pos.PropagateZ(dir, z1);
     922            if (fSlopeAbsorption)
     923                throw raytrace_user(kEnter+kAbsorbed, surface, kSlopeSurface,
     924                                    "EnterGroove - Photon absorbed by slope surface");
     925
     926            // Get the normal vector of the surface which was hit
     927            const VectorNorm norm(fGrooves[ix].slope.theta_norm+RandomTheta(fPSF),
     928                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     929
     930            // Get the optical transition of the direction vector
     931            const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
     932
     933            // Transition was Reflection - try again
     934            if (ret==1 || ret==2)
     935                return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
     936
     937            // Transition was Refraction - enter
     938            if (ret>=3)
     939                return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, pos.T())+1;
     940
     941            // Error occured (see ApplyTransition for details)
     942            throw raytrace_error(kEnter+kTransitionError, surface, kSlopeSurface,
     943                                 "EnterGroove - MOptics::ApplyTransition failed for slope surface");
     944        }
     945    }
     946
     947    // Photons was just injected (test both surfaces) or came from the other surface
     948    if (surface==kEntrySurface || surface==kSlopeSurface)
     949    {
     950        const double z2 = CalcIntersection(pos, dir, fGrooves[ix].draft);
     951
     952        // We hit the draft angle
     953        if (z2!=0)
     954        {
     955            // Move photon to new hit position
     956            pos.PropagateZ(dir, z2);
     957            if (fDraftAbsorption)
     958                throw raytrace_user(kEnter+kAbsorbed, surface, kDraftSurface,
     959                                    "EnterGroove - Photon absorbed by draft surface");
     960
     961            // Get the normal vector of the surface which was hit
     962            const VectorNorm norm(fGrooves[ix].draft.theta_norm+RandomTheta(fPSF),
     963                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     964
     965            // Get the optical transition of the direction vector
     966            const int ret = MOptics::ApplyTransition(dir, norm, 1, n0, fFresnelReflection);
     967
     968            // Transition was Reflection - try again
     969            if (ret==1 || ret==2)
     970                return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
     971
     972            // Transition was Refraction - enter
     973            if (ret>=3)
     974                return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, pos.T())+1;
     975
     976            // Error occured (see ApplyTransition for details)
     977            throw raytrace_error(kEnter+kTransitionError, surface, kDraftSurface,
     978                                 "EnterGroove - MOptics::ApplyTransition failed for draft surface");
     979        }
     980    }
     981
     982    if (dir.Z()>0)
     983    {
     984        // We have missed both surfaces and we are upgoing...
     985        // ... ray can be discarded
     986        throw raytrace_info(kEnter+kStrayUpgoing, surface, kNoSurface,
     987                            "EnterGroove - Particle is upgoing and has hit no surface");
     988    }
     989
     990    // The ray has left the peak at the bottom(?)
     991    throw raytrace_error(kEnter+kStrayDowngoing, surface, kNoSurface,
     992                         "EnterGroove - Particle is downgoing and has hit no surface");
     993}
     994
     995/*
     996// Leave the peak from inside the material, either thought the draft surface or the
     997// slope surface or the bottom connecting the valley of both
     998int MFresnelLens::LeavePeak(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir, double T0) const
     999{
     1000    const double rx = pos.R();
     1001
     1002    if (rx>=fR)
     1003    {
     1004        //cout << "Left the lens radius (leave)" << endl;
     1005        throw -10;
     1006    }
     1007
     1008    if (dir.Z()>0 && surface!=3) // && surface!=4)
     1009    {
     1010        //cout << "Upgoing, inside of the material" << endl;
     1011        //pos.PropagateZ(dir, dir.Z()>0 ? 3 : -3);
     1012        throw -11;
     1013    }
     1014
     1015    if (surface!=1 && surface!=2 && surface!=3) // && surface!=4)
     1016    {
     1017        //cout << "Surface of origin invalid" << endl;
     1018        throw -12;
     1019    }
     1020
     1021
     1022    // Calculate the ordinal number of the groove correpsonding to rx
     1023    const int ix = TMath::FloorNint(rx/fW);
     1024
     1025    // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
     1026
     1027    Cone slope = fGrooves[ix].slope;
     1028    Cone draft = fGrooves[ix].draft;
     1029
     1030    const bool is_draft = rx>fGrooves[ix].r;
     1031    if (is_draft)
     1032    {
     1033        // We are in the volume under the draft angle... taking the slope from ix+1
     1034        if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
     1035            slope = fGrooves[ix+1].slope;
     1036    }
     1037    else
     1038    {
     1039        // We are in the volume under the slope angle... taking the draft from ix-1
     1040        if (ix>0) // FIXME: Check whether this is correct
     1041            draft = fGrooves[ix-1].draft;
     1042    }
     1043
     1044    if (is_draft+1!=surface && (surface==1 || surface==2))
     1045        cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
     1046
     1047    if (surface==3)
     1048    {
     1049        //cout << "Upgoing, coming from the bottom of the lens" << endl;
     1050        // Find out which triangle (peak) the photon is going to enter
     1051        // then proceed...
     1052        throw -13;
     1053    }
     1054
     1055
     1056    // We are inside the material and downgoing, so if we come from a slope surface,
     1057    // we can only hit a draft surface after and vice versa
     1058    if (is_draft || surface==3)
     1059    {
     1060        const double z1 = CalcIntersection(pos, dir, slope);
     1061
     1062        // We hit the slope angle and are currently in the volume under the draft surface
     1063        if (z1!=0)
     1064        {
     1065            // Move photon to new hit position
     1066            pos.PropagateZ(dir, z1);
     1067
     1068            if (fSlopeAbsorption)
     1069                throw -200;
     1070
     1071            // Get the normal vector of the surface which was hit
     1072            const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
     1073                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     1074
     1075            // Get the optical transition of the direction vector
     1076            const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
     1077
     1078            // Transition was Reflection - try again
     1079            if (ret==1 || ret==2)
     1080                return LeavePeak(1, n0, lambda, pos, dir, T0)+1;
     1081
     1082            // Transition was Refraction - leave
     1083            if (ret>=3)
     1084            {
     1085                if (!Transmission(pos.T()-T0, lambda))
     1086                    throw -14;
     1087
     1088                return EnterGroove(1, n0, lambda, pos, dir)+1;
     1089            }
     1090
     1091            // Error occured (see ApplyTransition for details)
     1092            //cout << "ERR[TIR3]" << endl;
     1093            throw -15;
     1094        }
     1095    }
     1096
     1097    if (!is_draft || surface==3)
     1098    {
     1099        const double z2 = CalcIntersection(pos, dir, draft);
     1100
     1101        // We hit the draft angle from the inside and are currently in the volume under the slope angle
     1102        if (z2!=0)
     1103        {
     1104            // Move photon to new hit position
     1105            pos.PropagateZ(dir, z2);
     1106
     1107            if (fDraftAbsorption)
     1108                throw -201;
     1109
     1110            // Get the normal vector of the surface which was hit
     1111            const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
     1112                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     1113
     1114            // Get the optical transition of the direction vector
     1115            const int ret = MOptics::ApplyTransition(dir, norm, n0, 1);
     1116
     1117            // Transition was Reflection - try again
     1118            if (ret==1 || ret==2)
     1119                return LeavePeak(2, n0, lambda, pos, dir, T0)+1;
     1120
     1121            // Transition was Refraction - leave
     1122            if (ret>=3)
     1123            {
     1124                if (!Transmission(pos.T()-T0, lambda))
     1125                    throw -16;
     1126
     1127                return EnterGroove(2, n0, lambda, pos, dir)+1;
     1128            }
     1129
     1130            // Error occured (see ApplyTransition for details)
     1131            //cout << "ERR[TIR4]" << endl;
     1132            throw -17;
     1133        }
     1134    }
     1135
     1136    if (surface==3)// || surface==4)
     1137    {
     1138        //cout << ix << " Lost bottom reflected ray " << surface << endl;
     1139        throw -18;
     1140    }
     1141
     1142    // The ray has left the peak at the bottom
     1143
     1144    // FIXME: There is a tiny chance to escape to the side
     1145    // As there is a slope in the bottom surface of the peak
     1146
     1147    // Move photon to new hit position
     1148    pos.PropagateZ(dir, -fH);
     1149
     1150    if (pos.R()>fR)
     1151    {
     1152        //cout << "Left the lens radius (bottom)" << endl;
     1153        throw -19;
     1154    }
     1155
     1156    // Get the normal vector of the surface which was hit
     1157    const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
     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
     1163    // (Photon scattered back from the bottom of the lens)
     1164    if (ret==1 || ret==2)
     1165        return LeavePeak(3, n0, lambda, pos, dir, T0)+1;
     1166
     1167    // Transition was Refraction
     1168    // (Photon left at the bottom of the lens)
     1169    if (ret>=3)
     1170    {
     1171        if (!Transmission(pos.T()-T0, lambda))
     1172            throw -20;
     1173
    2891174        return 0;
    290 
    291     const double sqrtBac = sqrt(B2 - a*c);
    292 
    293     if (a==0)
    294         return 0;
    295 
    296     const int sign = g.fPeakZ>=0 ? 1 : -1;
    297 
    298     const double dz[2] =
    299     {
    300         (sqrtBac+B) / a * sign,
    301         (sqrtBac-B) / a * sign
    302     };
    303 
    304     // Only one dz[i] can be within the lens (due to geometrical reasons
    305 
    306     if (dz[0]<0 && dz[0]>fThickness)
    307         return dz[0];
    308 
    309     if (dz[1]<0 && dz[1]>fThickness)
    310         return dz[1];
    311 
    312     return 0;
    313 }
     1175    }
     1176
     1177    // Error occured (see ApplyTransition for details)
     1178    //cout << "ERR[TIR5]" << endl;
     1179    throw -21;
     1180}*/
     1181
     1182// Leave the peak from inside the material, either thought the draft surface or the
     1183// slope surface or the bottom connecting the valley of both
     1184int MFresnelLens::LeavePeak(int surface, double n0, MQuaternion &pos, MQuaternion &dir, double T0) const
     1185{
     1186    const double rx = pos.R();
     1187
     1188    if (rx>=fR) // This is an error as the direction vector is now invalid
     1189        throw raytrace_error(kLeave+kOutsideRadius, surface, kNoSurface,
     1190                             "LeavePeak - Surface hit outside allowed radius");
     1191
     1192    // FIXME: Can we track them further?
     1193    if (fDisableMultiEntry && dir.Z()>0 && surface!=3/* && surface!=4*/)
     1194        throw raytrace_info(kLeave+kStrayUpgoing, surface, kNoSurface,
     1195                            "LeavePeak - Particle is upgoing inside the material and does not come from the bottom");
     1196
     1197    if (surface!=kSlopeSurface && surface!=kDraftSurface && surface!=kExitSurface/* && surface!=4*/)
     1198        throw raytrace_error(kLeave+kInvalidOrigin, surface, kNoSurface,
     1199                             "LeavePeak - Invalid surface of origin");
     1200
     1201
     1202    // Calculate the ordinal number of the groove correpsonding to rx
     1203    const int ix = TMath::FloorNint(rx/fW);
     1204
     1205    // FIXME: The Z-coordinate (cone.h) is actually a line through two points!!!
     1206
     1207    Cone slope = fGrooves[ix].slope;
     1208    Cone draft = fGrooves[ix].draft;
     1209
     1210    //if (is_draft+1!=surface && (surface==1 || surface==2))
     1211    //    cout << "SURFACE: " << is_draft+1 << " " << surface << endl;
     1212
     1213    const bool is_draft = rx>fGrooves[ix].r;
     1214    if (is_draft)
     1215    {
     1216        // We are in the volume under the draft angle... taking the slope from ix+1
     1217        if (ix<fGrooves.size()-1) // FIXME: Does that make sense?
     1218            slope = fGrooves[ix+1].slope;
     1219    }
     1220    else
     1221    {
     1222        // We are in the volume under the slope angle... taking the draft from ix-1
     1223        if (ix>0) // FIXME: Check whether this is correct
     1224            draft = fGrooves[ix-1].draft;
     1225    }
     1226
     1227    if (surface==kExitSurface)
     1228    {
     1229        if (!fBottomReflection)
     1230            throw raytrace_user(kLeave+kAbsorbed, surface, kExitSurface,
     1231                                "LeavePeak - Particle absorbed on the bottom");
     1232
     1233        const int in = FindPeak(ix, pos, dir);
     1234
     1235        // This might happen if the ray is very flat and leaving
     1236        // the lens before hitting the border boundary of the grooves
     1237        if (in<0)
     1238            throw raytrace_error(kLeave+kNoSurfaceFound, kExitSurface, kNoSurface,
     1239                                 "LeavePeak - No hit surface found for particle reflected at the bottom");
     1240
     1241        slope = fGrooves[in].slope;
     1242        draft = fGrooves[in==0 ? 0 : in-1].draft;
     1243    }
     1244
     1245    // FIXME: There is a chance that we can hit the same surface twice (for very horizontal rays
     1246    //        but this requires a proper selection of the hit point
     1247
     1248    // We are inside the material and downgoing, so if we come from a slope surface,
     1249    // we can only hit a draft surface after and vice versa
     1250    if (is_draft || surface==kExitSurface)
     1251    {
     1252        const double z1 = CalcIntersection(pos, dir, slope);
     1253
     1254        // We hit the slope angle and are currently in the volume under the draft surface
     1255        if (z1!=0)
     1256        {
     1257            // Move photon to new hit position
     1258            pos.PropagateZ(dir, z1);
     1259
     1260            if (fSlopeAbsorption)
     1261                throw raytrace_user(kLeave+kAbsorbed, surface, kSlopeSurface,
     1262                                    "LeavePeak - Photon absorbed by slope surface");
     1263
     1264            // Get the normal vector of the surface which was hit
     1265            const VectorNorm norm(slope.theta_norm+RandomTheta(fPSF),
     1266                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     1267
     1268            // Get the optical transition of the direction vector
     1269            const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
     1270
     1271            // Transition was Reflection - try again
     1272            if (ret==1 || ret==2)
     1273                return -kSlopeSurface;//LeavePeak(1, n0, lambda, pos, dir, T0)+1;
     1274
     1275            // Transition was Refraction - leave
     1276            if (ret>=3) // Transmission
     1277                return kSlopeSurface;//EnterGroove(1, n0, lambda, pos, dir)+1;
     1278
     1279            // Error occured (see ApplyTransition for details)
     1280            throw raytrace_error(kLeave+kTransitionError, surface, kSlopeSurface,
     1281                                 "LeavePeak - MOptics::ApplyTransition failed for slope surface");
     1282        }
     1283    }
     1284
     1285    if (!is_draft || surface==kExitSurface)
     1286    {
     1287        const double z2 = CalcIntersection(pos, dir, draft);
     1288
     1289        // We hit the draft angle from the inside and are currently in the volume under the slope angle
     1290        if (z2!=0)
     1291        {
     1292            // Move photon to new hit position
     1293            pos.PropagateZ(dir, z2);
     1294
     1295            if (fDraftAbsorption)
     1296                throw raytrace_user(kLeave+kAbsorbed, surface, kDraftSurface,
     1297                                    "LeavePeak - Photon absorbed by draft surface");
     1298
     1299            // Get the normal vector of the surface which was hit
     1300            const VectorNorm norm(draft.theta_norm+RandomTheta(fPSF),
     1301                                  pos.XYvector().Phi()+RandomPhi(pos.R(), fPSF));
     1302
     1303            // Get the optical transition of the direction vector
     1304            const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
     1305
     1306            // Transition was Reflection - try again
     1307            if (ret==1 || ret==2)
     1308                return -kDraftSurface;//LeavePeak(2, n0, lambda, pos, dir, T0)+1;
     1309
     1310            // Transition was Refraction - leave
     1311            if (ret>=3) // Transmission
     1312                return kDraftSurface;//EnterGroove(2, n0, lambda, pos, dir)+1;
     1313
     1314            // Error occured (see ApplyTransition for details)
     1315            //cout << "ERR[TIR4]" << endl;
     1316            throw raytrace_error(kLeave+kTransitionError, surface, kDraftSurface,
     1317                                 "LeavePeak - MOptics::ApplyTransition failed for draft surface");
     1318        }
     1319    }
     1320
     1321    if (surface==kExitSurface/* || surface==4*/)
     1322        throw raytrace_error(kLeave+kFoundSurfaceUnavailable, kExitSurface, is_draft?kSlopeSurface:kDraftSurface,
     1323                             "LeavePeak - Ray reflected on the bottom did not hit the found surface");
     1324
     1325    // The ray has left the peak at the bottom
     1326
     1327    // FIXME: There is a tiny chance to escape to the side
     1328    // As there is a slope in the bottom surface of the peak
     1329
     1330    // FIXME: Theoretically, a ray can hit the same surface twice
     1331
     1332    // Move photon to new hit position
     1333    pos.PropagateZ(dir, -fH);
     1334
     1335    if (pos.R()>fR)
     1336        throw raytrace_info(kLeave+kOutsideRadius, surface, kExitSurface,
     1337                            "LeavePeak - Hit point at the bottom surface is beyond allowed radius");
     1338
     1339    // Get the normal vector of the surface which was hit
     1340    const VectorNorm norm(RandomTheta(fPSF), gRandom->Uniform(0, TMath::TwoPi()));
     1341
     1342    // Get the optical transition of the direction vector
     1343    const int ret = MOptics::ApplyTransition(dir, norm, n0, 1, fFresnelReflection);
     1344
     1345    // Transition was Reflection
     1346    // (Photon scattered back from the bottom of the lens)
     1347    if (ret==1 || ret==2)
     1348        return -kExitSurface;//LeavePeak(3, n0, lambda, pos, dir, T0)+1;
     1349
     1350    // Transition was Refraction
     1351    // (Photon left at the bottom of the lens)
     1352    if (ret>=3) // Transmission
     1353        return kPhotonHasLeft;
     1354
     1355    // Error occured (see ApplyTransition for details)
     1356    throw raytrace_error(kLeave+kTransitionError, surface, kExitSurface, "LeavePeak - MOptics::ApplyTransition failed for bottom surface");
     1357}
     1358
     1359
     1360// Differences:
     1361// Returns a 'reflected' vector at z=0
     1362// Does not propagate to z=0 at the beginning
     1363Int_t MFresnelLens::ExecuteOptics(MQuaternion &p, MQuaternion &u, const Short_t &wavelength) const
     1364{
     1365    // Corsika Coordinates are in cm!
     1366
     1367    const double lambda = wavelength==0 ? fLambda : wavelength;
     1368    if (fAbsorptionLength.GetNp()!=0 &&
     1369        (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
     1370        {
     1371            gLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
     1372            return -1;
     1373        }
     1374
     1375    const double n0 = MFresnelLens::RefractiveIndex(lambda);
     1376
     1377    try
     1378    {
     1379        int last_surface = EnterGroove(kEntrySurface, n0, p, u);
     1380
     1381        // last_surface that was hit (photon originates from)
     1382        //  0 entrance (Z=0) or exit (Z=-fH) surface
     1383        //  1 slope
     1384        //  2 draft
     1385        //  3 bottom
     1386        // positive: photon is outside of material  -->  Try to enter
     1387        // nagative: photon is inside  of material  -->  Try to leave
     1388
     1389        double T0 = 0;
     1390
     1391        // The general assumption is: no surface can be hit twice in a row
     1392
     1393        int cnt = 0;
     1394        while (last_surface!=0)
     1395        {
     1396            cnt ++;
     1397
     1398            // photon is outside of material --> try to enter
     1399            if (last_surface>0)
     1400            {
     1401                last_surface = EnterGroove( last_surface, n0, p, u);
     1402
     1403                // successfully entered --> remember time of entrance to calculate transimission
     1404                if (last_surface<0)
     1405                    T0 = p.T();
     1406
     1407                continue;
     1408            }
     1409
     1410            // photon is inside of material --> try to leave
     1411            if (last_surface<0)
     1412            {
     1413                last_surface = LeavePeak(-last_surface, n0, p, u, T0);
     1414
     1415                // successfully left --> apply transmission
     1416                if (last_surface>=0)
     1417                {
     1418                    if (!Transmission(p.T()-T0, lambda))
     1419                        throw raytrace_error(kAbsorbed, last_surface, kMaterial,
     1420                                             "TraceRay - Ray absorbed in material");
     1421                }
     1422
     1423                continue;
     1424            }
     1425        }
     1426
     1427        // To make this consistent with a mirror system,
     1428        // we now change our coordinate system
     1429        // Rays from the lens to the camera are up-going (positive sign)
     1430        u.fVectorPart.SetZ(-u.Z());
     1431
     1432        // In the datasheet, it looks as if F is calculated
     1433        // towards the center of the lens
     1434        // (Propagating to F means not propagating a distance of F-H/2)
     1435        p.fVectorPart.SetZ(0);
     1436
     1437        return cnt>=fMinHits && (fMaxHits==0 || cnt<=fMaxHits) ? cnt : -1;;
     1438    }
     1439    catch (const raytrace_exception &e)
     1440    {
     1441        return -e.id();
     1442    }
     1443
     1444/*
     1445    try
     1446    {
     1447        const int cnt = EnterGroove(0, n0, lambda, p, u);
     1448
     1449        // To make this consistent with a mirror system,
     1450        // we now change our coordinate system
     1451        // Rays from the lens to the camera are up-going (positive sign)
     1452        u.fVectorPart.SetZ(-u.Z());
     1453
     1454        // In the datasheet, it looks as if F is calculated
     1455        // towards the center of the lens
     1456        // (Propagating to F means not propagating a distance of F-H/2)
     1457        p.fVectorPart.SetZ(0);
     1458
     1459        return cnt>=fMinHits && (fMaxHits==0 || cnt<=fMaxHits) ? cnt : -1;
     1460
     1461    }
     1462    catch (const int &rc)
     1463    {
     1464        return rc;
     1465    }
    3141466*/
    315 
    316 Int_t MFresnelLens::ExecuteOptics(MQuaternion &p, MQuaternion &u, const Short_t &wavelength) const
     1467}
     1468
     1469// Differences:
     1470// Does propagate to z=0 at the beginning
     1471Int_t MFresnelLens::TraceRay(vector<MQuaternion> &vec, MQuaternion &p, MQuaternion &u, const Short_t &wavelength, bool verbose) const
    3171472{
    3181473    // Corsika Coordinates are in cm!
    3191474
    320     const double D = 54.92;  // [cm] Diameter
    321     const double F = 50.21;  // [cm] focal length (see also MGeomCamFAMOUS!)
    322 
    323     const double R = D/2;    // [cm] radius of lens
    324     const double w = 0.01;   // [cm] Width of a single groove
    325     const double H = 0.25;   // [cm] Thickness (from pdf)
    326 
    327     // Focal length is given at this wavelength
    328     const double n0 = RefractiveIndex(546);
    329     const double n  = RefractiveIndex(wavelength==0 ? 546 : wavelength);
    330 
    331     const double r = p.R();
    332 
    333     // Ray has missed the lens
    334     if (r>R)
    335         return -1;
    336 
    337     // Calculate the ordinal number of the groove which is hit
    338     const unsigned int nth = TMath::FloorNint(r/w);
    339 
    340     // Calculate its lower and upper radius and its center
    341     const double r0 = nth*w;       // Lower radius of nth groove
    342     const double rc = nth*w + w/2; // Upper radius of nth groove
    343     const double r1 = nth*w + w;   // Center of nth groove
    344 
    345     // Calculate the slope and draft angles
    346     const double alpha = -SlopeAngle(rc, F, n0);
    347     const double psi   =  DraftAngle(r1);
    348 
    349     // Define the corresponding surfaces
    350     const Groove slope(TMath::Pi()/2-alpha,  r0*tan(alpha));
    351     const Groove draft(-psi,                -r1/tan(psi));
    352 
    353     // Calculate the insersection of the ray between the two surfaces
    354     // with z between 0 and -H
    355     const double dz_slope = CalcIntersection(p, u, slope, -H);
    356     const double dz_draft = CalcIntersection(p, u, draft, -H);
    357 
    358     // valid means that the photon has hit the fresnel surface,
    359     // otherwise the draft surface
    360     bool valid = dz_slope>dz_draft || dz_draft>=0;
    361 
    362     // Theta angle of the normal vector of the surface that was hit
    363     TVector3 norm;
    364 
    365     // Propagate to the hit point. To get the correct normal vector of
    366     // the surface, the hit point must be knwone
    367     p.PropagateZ(u, valid ? dz_slope : dz_draft);
    368     norm.SetMagThetaPhi(1, valid ? alpha : psi-TMath::Pi()/2, p.fVectorPart.Phi());
    369 
    370     // Estimate reflectivity for this particular hit (n1<n2 => check before)
    371     // Probability that the ray gets reflected
    372     if (gRandom->Uniform()<SchlickReflectivity(u.fVectorPart, norm, 1, n))
    373     {
    374         // Reflect at the surface
    375         u *= MReflection(norm);
    376 
    377         // Upgoing rays are lost
    378         if (u.Z()>0)
    379             return valid ? -3 : -4;
    380 
    381         // Propgate back to z=0 (FIXME: CalcIntersection requires z=0)
    382         p.PropagateZ(u, 0);
    383 
    384         // Calc new intersection the other of the two surfaces
    385         valid = !valid;
    386 
    387         const double dz = CalcIntersection(p, u, valid ? slope : draft, -H);
    388 
    389         // Propagate to the hit point at z=dz (dz<0) and get the new normal vector
    390         p.PropagateZ(u, dz);
    391         norm.SetMagThetaPhi(1, valid ? alpha : psi-TMath::Pi()/2, p.fVectorPart.Phi());
    392     }
    393 
    394     const double check_r = p.R();
    395 
    396     // Some sanity checks (we loose a few permille due to numerical uncertainties)
    397     // If the ray has not hit within the right radii.. reject
    398     if (check_r<r0)
    399         return valid ? -5 : -6;
    400 
    401     if (check_r>r1)
    402         return valid ? -7 : -8;
    403 
    404     // Find dw value of common z-value
    405     //
    406     // gz*tan(psi) = w - gw
    407     // gz = dw*tan(alpha)
    408     //
    409     const double Gw =  w/(tan(alpha)*tan(psi)+1);
    410     const double Gz = -Gw*tan(alpha);
    411 
    412     if (valid && check_r>r0+Gw)
    413         return -9;
    414     if (!valid && check_r<r0+Gw)
    415         return -10;
    416 
    417     // Apply refraction at the lens entrance surface (changes directional vector)
    418     // FIXME: Surace roughness
    419     if (!ApplyRefraction(u, norm, 1, n))
    420         return valid ? -11 : -12;
    421 
    422     // Propagate ray until bottom of lens
    423     p.PropagateZ(u, -H);
    424 
    425     // The lens exit surface is a plane in X/Y at Z=-H
    426     norm = TVector3(0, 0, 1);
    427 
    428     // Apply refraction at the lens exit surface (changes directional vector)
    429     // FIXME: Surace roughness
    430     if (!ApplyRefraction(u, norm, n, 1))
    431         return valid ? -13 : -14;
    432 
    433     // Estimate reflectivity for this particular hit (n1>n2 => check after)
    434     // Probability that the ray gets reflected
    435     // (total internal reflection -> we won't track that further)
    436     if (gRandom->Uniform()<SchlickReflectivity(u.fVectorPart, norm, n, 1))
    437         return valid ? -15 : -16;
    438 
    439     // To make this consistent with a mirror system,
    440     // we now change our coordinate system
    441     // Rays from the lens to the camera are up-going (positive sign)
    442     u.fVectorPart.SetZ(-u.Z());
    443 
    444     // In the datasheet, it looks as if F is calculated
    445     // towards the center of the lens
    446     // (Propagating to F means not propagating a distance of F-H/2)
    447     p.fVectorPart.SetZ(H/2);
    448 
    449     return nth; // Keep track of groove index
    450 }
     1475    const double lambda = wavelength==0 ? fLambda : wavelength;
     1476    if (fAbsorptionLength.GetNp()!=0 &&
     1477        (lambda<fAbsorptionLength.GetXmin() || lambda>fAbsorptionLength.GetXmax()))
     1478        {
     1479            gLog << err << "Wavelength " << lambda << "nm out of absorption range [" << fAbsorptionLength.GetXmin() << "nm;" << fAbsorptionLength.GetXmax() << "nm]" << endl;
     1480            return -1;
     1481        }
     1482
     1483    const double n0 = MFresnelLens::RefractiveIndex(lambda);
     1484
     1485    // Photon must be at the lens surface
     1486    p.PropagateZ(u, 0);
     1487    vec.push_back(p);
     1488
     1489    try
     1490    {
     1491        int last_surface = EnterGroove(kEntrySurface, n0, p, u);
     1492        //cout << "enter1 = " << last_surface << endl;
     1493
     1494        // last_surface that was hit (photon originates from)
     1495        //  0 entrance (Z=0) or exit (Z=-fH) surface
     1496        //  1 slope
     1497        //  2 draft
     1498        //  3 bottom
     1499        // positive: photon is outside of material  -->  Try to enter
     1500        // nagative: photon is inside  of material  -->  Try to leave
     1501
     1502        double T0 = 0;
     1503
     1504        // The general assumption is: no surface can be hit twice in a row
     1505
     1506        int cnt = 0;
     1507        while (last_surface!=0)
     1508        {
     1509            cnt ++;
     1510            vec.push_back(p);
     1511
     1512            // photon is outside of material --> try to enter
     1513            if (last_surface>0)
     1514            {
     1515                last_surface = EnterGroove( last_surface, n0, p, u);
     1516                //cout << "enter = " << last_surface << endl;
     1517
     1518                // successfully entered --> remember time of entrance to calculate transimission
     1519                if (last_surface<0)
     1520                    T0 = p.T();
     1521
     1522                continue;
     1523            }
     1524
     1525            // photon is inside of material --> try to leave
     1526            if (last_surface<0)
     1527            {
     1528                last_surface = LeavePeak(-last_surface, n0, p, u, T0);
     1529                //cout << "leave = " << last_surface << endl;
     1530
     1531                // successfully left --> apply transmission
     1532                if (last_surface>=0)
     1533                {
     1534                    if (!Transmission(p.T()-T0, lambda))
     1535                        throw raytrace_error(kAbsorbed, last_surface, kMaterial,
     1536                                             "TraceRay - Ray absorbed in material");
     1537                }
     1538
     1539                continue;
     1540            }
     1541        }
     1542
     1543        vec.push_back(p);
     1544        return cnt;
     1545    }
     1546    catch (const raytrace_exception &e)
     1547    {
     1548        if (verbose)
     1549            gLog << all << e.id() << ": " << e.what() << endl;
     1550
     1551        // Hit point at bottom surface beyond allowed range
     1552        // FIXME: Only if surface is kExitSurface
     1553        if (e.id()==2342)
     1554            vec.push_back(p);
     1555
     1556        return -e.id();
     1557    }
     1558}
  • trunk/Mars/msimreflector/MFresnelLens.h

    r19632 r19638  
    11#ifndef MARS_MFresnelLens
    22#define MARS_MFresnelLens
     3
     4#include <TVector3.h>
    35
    46#ifndef MARS_MOptics
    57#include "MOptics.h"
    68#endif
    7 
    8 class TVector3;
    9 class MQuaternion;
     9#ifndef MARS_MSpline3
     10#include "MSpline3.h"
     11#endif
     12#ifndef MARS_MQuaternion
     13#include "MQuaternion.h"
     14#endif
    1015
    1116class MFresnelLens : public MOptics
    1217{
     18public:
     19    // --------------- Helper class ---------------------
     20    // This is a TVector3 which is nromalized and takes theta and phi as arguent
     21    // same as calling
     22    // TVector3 v;
     23    // v.SetMagThetaPhi(1, theta, phi);
     24    class VectorNorm : public TVector3
     25    {
     26    public:
     27        VectorNorm(double theta, double phi) :
     28            TVector3(
     29                     sin(theta) * cos(phi),
     30                     sin(theta) * sin(phi),
     31                     cos(theta)
     32                    )
     33        {
     34        }
     35    };
     36
     37    // ----------- Properties of the grooves -------------
     38
     39    struct Cone
     40    {
     41        double z;          // z-coordinate of the peak of the cone (x=y=0)
     42        double theta;      // Opening angle of the cone [rad]
     43        double tan_theta;  // tan(theta)
     44        double tan_theta2; // tan(theta)*tan(theta)
     45        double h;          // height of the slope (intersection point in the valley), h<0
     46        double theta_norm; // theta angle of the normal vector corresponding to the cone surface
     47    };
     48
     49    struct Groove
     50    {
     51        double r;    // Radius of the intersection point between slope and draft angle
     52
     53        Cone slope;  // Description of the slope angle
     54        Cone draft;  // Description of the draft angle
     55    };
     56
    1357private:
    14     Double_t fMaxR;
     58
     59    // --------------------------------------------------
     60
     61    Double_t fMaxR;  //!
     62
     63    // ------------- Properties of the lens --------------
     64
     65    double fF;      // Focal length
     66    double fR;      // Radius of lens
     67    double fW;      // Width of groove
     68    double fH;      // Thickness of lens
     69    double fN;      //! Reference refractive index for lens design
     70    double fLambda; // Wavelength [nm] corresponding to fN (at which lens is optimized)
     71    double fVc;     //! Velocity of light within the lens material [cm/ns]
     72    double fPSF;    // Measure for the surface roughness
     73
     74    std::vector<Groove> fGrooves; //! Collection of all grooves
     75
     76    // ----------- Properties of the material -------------
     77
     78    MSpline3 fAbsorptionLength;   // Spline storing the absorption length vs wavelength
     79
     80    // ----------------------------------------------------
     81
     82    bool fSlopeAbsorption;   //! Absorb rays which hit the slope surface
     83    bool fDraftAbsorption;   //! Absorb rays which hit the draft surface
     84    bool fBottomReflection;  //! Absorb rays which would be reflected at the exit surface
     85    bool fDisableMultiEntry; //! Absorb upgoing rays inside the material which do not originate from the exit surface
     86    bool fFresnelReflection; //! Disable Fresnel reflection
     87    UInt_t fMinHits;         //! Minimum number of surface contacts to define a successfull passage
     88    UInt_t fMaxHits;         //! Maximum number of surface contacts to define a successfull passage (0=unlimited)
    1589
    1690    void InitMaxR();
     91    void InitGeometry(double maxr, double width, double N0, double F, double d);
     92    bool Transmission(double dt, double lambda) const;
     93
     94    double CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Cone &cone) const;
     95    int FindPeak(size_t i, const MQuaternion &p, const MQuaternion &u) const;
     96
     97    //int EnterGroove(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir) const;
     98    //int LeavePeak(int surface, double n0, double lambda, MQuaternion &pos, MQuaternion &dir, double T0) const;
     99
     100    int EnterGroove(int surface, double n0, MQuaternion &pos, MQuaternion &dir) const;
     101    int LeavePeak(int surface, double n0, MQuaternion &pos, MQuaternion &dir, double T0) const;
    17102
    18103public:
     
    25110
    26111    Int_t ExecuteOptics(MQuaternion &p, MQuaternion &u, const Short_t &) const;
     112    Int_t TraceRay(std::vector<MQuaternion> &vec, MQuaternion &p, MQuaternion &u, const Short_t &wavelength, bool verbose=false) const;
    27113
    28     Bool_t IsValid() const { return kTRUE; }
     114    Bool_t IsValid() const { return fGrooves.size(); }
     115
     116    // -----------------------------------------------------------
     117
     118    void SetPSF(const double &psf) { fPSF = psf; }
     119    void DefineLens(double F=50.21, double D=54.92, double w=0.01, double h=0.25, double lambda=546);
     120
     121    Int_t ReadTransmission(const TString &file, float thickness, bool correction=true);
     122
     123    // -----------------------------------------------------------
     124
     125    void EnableSlopeAbsorption(bool abs=true)    { fSlopeAbsorption   =  abs; }
     126    void EnableDraftAbsorption(bool abs=true)    { fDraftAbsorption   =  abs; }
     127    void DisableBottomReflection(bool ref=true)  { fBottomReflection  = !ref; }
     128    void DisableMultiEntry(bool mul=true)        { fDisableMultiEntry =  mul; }
     129    void DisableFresnelReflection(bool ref=true) { fFresnelReflection = !ref; }
     130
     131    void SetMinHits(UInt_t min) { fMinHits = min; }
     132    void SetMaxHits(UInt_t max) { fMaxHits = max; }
     133
     134    // -----------------------------------------------------------
     135
     136    const std::vector<MFresnelLens::Groove> GetGrooves() const { return fGrooves; }
     137    const Groove &GetGroove(int i) const { return fGrooves[i]; }
     138    const Groove &operator[](int i) const { return fGrooves[i]; }
     139    size_t GetNumGrooves() const { return fGrooves.size(); }
    29140
    30141    // -----------------------------------------------------------
    31142
    32143    static double RefractiveIndex(double lambda);
    33     static double SlopeAngle(double r, double F, double n);
     144    static double SlopeAngle(double r, double F, double n, double d);
    34145    static double DraftAngle(double r);
    35146
    36     ClassDef(MFresnelLens, 1) // Parameter container storing the description of a lens
     147    static double SlopeAngleParabolic(double r, double F, double n0, double n1, double d);
     148    static double SlopeAngleAspherical(double r);
     149    static double SlopeAngleOptimized(double r, double F, double n);
     150
     151    double SlopeAngle(const double &r) const
     152    {
     153        return SlopeAngle(r, fF, fN, fH);
     154    }
     155
     156    // -----------------------------------------------------------
     157
     158    ClassDef(MFresnelLens, 1) // Parameter container storing the description of a fresnel lens
    37159};
    38160   
Note: See TracChangeset for help on using the changeset viewer.