Changeset 19638
- Timestamp:
- 09/15/19 14:22:38 (5 years ago)
- Location:
- trunk/Mars/msimreflector
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Mars/msimreflector/MFresnelLens.cc
r19632 r19638 27 27 // MFresnelLens 28 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 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 31 37 // 32 38 ////////////////////////////////////////////////////////////////////////////// … … 41 47 #include "MReflection.h" 42 48 49 #include "MMath.h" 50 43 51 #include "MLog.h" 44 52 #include "MLogManip.h" … … 48 56 using namespace std; 49 57 58 // ========================================================================== 59 60 enum 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 79 enum 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 94 class raytrace_exception : public runtime_error 95 { 96 protected: 97 int fError; 98 int fOrigin; 99 int fSurface; 100 101 public: 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 118 class raytrace_error : public raytrace_exception 119 { 120 public: 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 }; 126 class raytrace_info : public raytrace_exception 127 { 128 public: 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 }; 134 class raytrace_user : public raytrace_exception 135 { 136 public: 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 50 146 // -------------------------------------------------------------------------- 51 147 // 52 148 // Default constructor 53 149 // 54 MFresnelLens::MFresnelLens(const char *name, const char *title) 150 MFresnelLens::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) 55 154 { 56 155 fName = name ? name : "MFresnelLens"; 57 156 fTitle = title ? title : "Parameter container storing a collection of several mirrors (reflector)"; 58 157 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 // 176 void 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 // 201 void 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 // 270 Int_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 // ========================================================================== 61 327 62 328 // -------------------------------------------------------------------------- … … 78 344 79 345 return sqrt(1+c0+c1+c2); 346 } 347 348 // -------------------------------------------------------------------------- 349 // 350 // A Fresnel lens with parabolic surface calculated with the sagittag 351 // function (k=-1) and a correction for the thickness of the lens 352 // on the curvature. See also PhD thesis, Tim Niggemann ch. 7.1.1. 353 // 354 // 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 // 367 double 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 // 392 double 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); 80 400 } 81 401 … … 92 412 // Here, a thin lens is assumed 93 413 // 94 // The HAWC's Eye lens is an Orafol SC94395 // https://www.orafol.com/en/europe/products/optic-solutions/productlines#pl196 //97 414 // sin(omega) = r / sqrt(r^2+F^2) 98 415 // tan(alpha) = sin(omega) / [ 1 - sqrt(n^2-sin(omega)^2) ] … … 104 421 // distance) 105 422 // 106 double MFresnelLens::SlopeAngle(double r, double F, double n) 107 { 423 double MFresnelLens::SlopeAngleOptimized(double r, double F, double n) 424 { 425 // Use F+d/2 108 426 double so = r / sqrt(r*r + F*F); 109 427 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 // 434 double MFresnelLens::SlopeAngle(double r, double F, double n, double d) 435 { 436 return SlopeAngleParabolic(r, F, 1.0003, n, d); 110 437 } 111 438 … … 126 453 return (3 + r*0.473)*TMath::DegToRad(); // Range [0deg; 15deg] 127 454 } 455 456 // ========================================================================== 128 457 129 458 // -------------------------------------------------------------------------- … … 149 478 } 150 479 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 484 static 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 491 static 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 // 530 double MFresnelLens::CalcIntersection(const MQuaternion &p, const MQuaternion &u, const Cone &cone) const 531 { 532 const double &Z = cone.z; 192 533 193 534 const double Ux = u.X()/u.Z(); … … 198 539 const double pz = p.Z(); 199 540 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; 204 543 205 544 const double Ur2 = Ux*Ux + Uy*Uy; … … 209 548 const double cr2 = Ux*py - Uy*px; 210 549 211 const double a 212 const double b = Ur2*pz - Up2 - H*t2;213 214 const double h = H-pz;215 const double h2 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; 216 555 217 556 // [ -b +-sqrt(b^2 - 4 ac) ] / [ 2a ] 218 557 219 558 const double radix = (Ur2*h2 + 2*Up2*h + pr2)*t2 - cr2*cr2; 220 221 559 if (radix<0) 222 560 return 0; 223 561 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 228 568 }; 229 569 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 // 647 int 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 // 748 bool 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; 237 764 } 238 765 239 766 /* 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 771 int 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 886 int 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 998 int 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 289 1174 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 1184 int 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 1363 Int_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 } 314 1466 */ 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 1471 Int_t MFresnelLens::TraceRay(vector<MQuaternion> &vec, MQuaternion &p, MQuaternion &u, const Short_t &wavelength, bool verbose) const 317 1472 { 318 1473 // Corsika Coordinates are in cm! 319 1474 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 1 1 #ifndef MARS_MFresnelLens 2 2 #define MARS_MFresnelLens 3 4 #include <TVector3.h> 3 5 4 6 #ifndef MARS_MOptics 5 7 #include "MOptics.h" 6 8 #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 10 15 11 16 class MFresnelLens : public MOptics 12 17 { 18 public: 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 13 57 private: 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) 15 89 16 90 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; 17 102 18 103 public: … … 25 110 26 111 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; 27 113 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(); } 29 140 30 141 // ----------------------------------------------------------- 31 142 32 143 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); 34 145 static double DraftAngle(double r); 35 146 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 37 159 }; 38 160
Note:
See TracChangeset
for help on using the changeset viewer.