Deprecated/UpgradeASCIITests/G4TankSimulatorASCII/G4TankFastCerenkov.cc
Go to the documentation of this file.
1 //
2 // ********************************************************************
3 // * DISCLAIMER *
4 // * *
5 // * The following disclaimer summarizes all the specific disclaimers *
6 // * of contributors to this software. The specific disclaimers,which *
7 // * govern, are listed with their locations in: *
8 // * http://cern.ch/geant4/license *
9 // * *
10 // * Neither the authors of this software system, nor their employing *
11 // * institutes,nor the agencies providing financial support for this *
12 // * work make any representation or warranty, express or implied, *
13 // * regarding this software system or assume any liability for its *
14 // * use. *
15 // * *
16 // * This code implementation is the intellectual property of the *
17 // * GEANT4 collaboration. *
18 // * By copying, distributing or modifying the Program (or any work *
19 // * based on the Program) you indicate your acceptance of this *
20 // * statement, and all its terms. *
21 // ********************************************************************
22 //
23 // MODIFIED by P Skelton November 2003 - only use this as part of the
24 // G4FasterSimulatorModule in the Auger observatory simulation.
25 //
26 // Due to size, the code is split into several files. The subsidiary ones
27 // are #included into this file Subsidiary file: Propogate.icc
28 
29 #include "G4TankFastCerenkov.h"
30 #include "G4TankConstruction.h"
31 #include "G4TankPMT.h"
32 using namespace G4TankSimulatorASCII;
33 
34 #include <G4Version.hh>
35 
36 #include <fwk/CentralConfig.h>
37 
38 #include <utl/Reader.h>
39 #include <utl/AugerException.h>
40 #include <utl/ErrorLogger.h>
41 #include <utl/MathConstants.h>
42 #include <utl/AugerException.h>
43 
44 #include <iostream>
45 #include <vector>
46 #include <string>
47 
66 
67 
69 
72 
75 
78 
83 G4ThreeVector G4TankFastCerenkov::fPMTPos[4];
84 
85 
87 
88 
89 G4TankFastCerenkov::G4TankFastCerenkov(const G4String& processName)
90  : G4VContinuousProcess(processName)
91 {
92  fTrackSecondariesFirst = false;
93  fMaxPhotons = 0;
94  fThePhysicsTable = NULL;
95  if (verboseLevel > 0) G4cerr << GetProcessName() << " is created "
96  << G4endl;
98 
99  // Rayleigh Scattering data taken from G4OpRayleigh.cc
100  // isothermal compressibility of water
101  const G4double betat = 7.658e-23 * m3 / MeV;
102  // K Boltzman
103  const G4double kboltz = 8.61739e-11 * MeV / kelvin;
104  // Temperature of water is 10 degrees celsius
105  const G4double temp = 283.15 * kelvin;
106  const G4double hc4 = pow(h_Planck * c_light, 4); // constants defined
107  // in CLHEP header PhysicalConstants.h - included from globals.hh
108  fRayleighConst = 27.0 * hc4 / (8.0 * pow(utl::kPi, 3) * betat *
109  temp * kboltz);
110 
111 }
112 
114 
115  if (fThePhysicsTable != NULL) {
116 
117  fThePhysicsTable->clearAndDestroy();
118  delete fThePhysicsTable;
119  fThePhysicsTable = 0;
120 
121  }
122 
123 }
124 
126 
131 
134 
139 
144 
147 
153 
154  const sdet::Station *fCurrentDetectorStation = G4TankSimulator::GetCurrentDetectorStation();
155 
156  fQE = fCurrentDetectorStation->GetPMT(1).GetQuantumEfficiency();
157 
158  const double& collectionEfficiency =
159  fCurrentDetectorStation->GetPMT(1).GetCollectionEfficiency();
160 
161 
162 
163  std::vector<double>::iterator fIt;
164  for (fIt = fQE.YBegin(); fIt != fQE.YEnd(); fIt++)
165  (*fIt) *= collectionEfficiency; // checked - this is right
166 
167  for (fIt = fQE.XBegin(); fIt != fQE.XEnd(); fIt++)
168  (*fIt) *= eV;
169 
170  // We need to check that values have been imported correctly, and that
171  // e.g. water absorption length is correct and doesn't need scaling
172  // by a MaxAbsLength parameter (likewise quantum efficiency).
173  // NOW CHECKED - AbsLength is the actual absorption length, fQE is the
174  // actual fQE (not including collection efficiency)
175  //
176  // Also need to see what the Pmt positions are in the utl::Point structures.
177  // (need to specify coordinate system when accesing co-ordinate values)
178 
182 
183  const utl::CoordinateSystemPtr& pmt1Coord= pmt1.GetCoordinateSystem();
184  const utl::CoordinateSystemPtr& pmt2Coord= pmt2.GetCoordinateSystem();
185  const utl::CoordinateSystemPtr& pmt3Coord= pmt3.GetCoordinateSystem();
186 
187  fPMTPos[1].set(pmt1.GetX(pmt1Coord) * m,
188  pmt1.GetY(pmt1Coord) * m,
189  pmt1.GetZ(pmt1Coord) * m);
190 
191  fPMTPos[2].set(pmt2.GetX(pmt2Coord) * m,
192  pmt2.GetY(pmt2Coord) * m,
193  pmt2.GetZ(pmt2Coord) * m);
194 
195  fPMTPos[3].set(pmt3.GetX(pmt3Coord) * m,
196  pmt3.GetY(pmt3Coord) * m,
197  pmt3.GetZ(pmt3Coord) * m);
198 
199  for(unsigned int k=1; k<4; ++k)
200  fRoofPos[k] = fTankHeight + fTankThickness - fPMTPos[k].z();
201 
202  // checked above - PMTs are in expected positions
210  return;
211 
212 }
213 
214 G4bool G4TankFastCerenkov::IsApplicable(const G4ParticleDefinition& aParticle) {
215 
216  return (aParticle.GetPDGCharge() != 0);
217 
218 }
219 
221 
222  fTrackSecondariesFirst = state;
223 
224 }
225 
226 void G4TankFastCerenkov::SetMaxNumPhotonsPerStep(const G4int numPhotons) {
227 
228  fMaxPhotons = numPhotons;
229 
230 }
231 
232 G4PhysicsTable *G4TankFastCerenkov::GetPhysicsTable() const {
233 
234  return fThePhysicsTable;
235 
236 }
237 
239 
240  G4int PhysicsTableSize = fThePhysicsTable->entries();
241  G4PhysicsOrderedFreeVector *v;
242  //G4int i;
243 
244  for (G4int i = 0; i < PhysicsTableSize; ++i) {
245 
246  v = static_cast<G4PhysicsOrderedFreeVector *>((*fThePhysicsTable)[i]);
247  v->DumpValues();
248 
249  }
250 
251 }
252 
253 G4VParticleChange* G4TankFastCerenkov::AlongStepDoIt(const G4Track& aTrack,
254  const G4Step& aStep) {
255 
256 
257  const bool IsFromMuonDecay= aStep.GetTrack()->GetUserInformation();
258 
259  // std::vector<double>::iterator VecItPP;
260 
261  aParticleChange.Initialize(aTrack);
262  const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle();
263  const G4Material* aMaterial = aTrack.GetMaterial();
264  G4StepPoint* pPreStepPoint = aStep.GetPreStepPoint();
265  G4StepPoint* pPostStepPoint = aStep.GetPostStepPoint();
266 
267  const G4ThreeVector x0 = pPreStepPoint->GetPosition();
268  const G4ThreeVector p0 = aStep.GetDeltaPosition().unit();
269  const G4double t0 = pPreStepPoint->GetGlobalTime();
270 
271  if (t0 > 1.0e6) return G4VContinuousProcess::AlongStepDoIt(aTrack, aStep);
272 
273  G4ThreeVector aPrimaryDirection = pPreStepPoint->GetMomentumDirection();
274 
275  const G4double cosAlpha = aPrimaryDirection.z();
276  const G4double sinAlpha = sqrt(1.0 - (cosAlpha * cosAlpha));
277  const G4double cosBeta = aPrimaryDirection.x() / sinAlpha;
278  const G4double sinBeta = aPrimaryDirection.y() / sinAlpha;
279 
280  G4MaterialPropertiesTable* aMaterialPropertiesTable =
281  aMaterial->GetMaterialPropertiesTable();
282 
283  if (!aMaterialPropertiesTable)
284  return G4VContinuousProcess::AlongStepDoIt(aTrack, aStep);
285 
286  const G4MaterialPropertyVector* Rindex =
287  aMaterialPropertiesTable->GetProperty("RINDEX");
288 
289  if (!Rindex)
290  return G4VContinuousProcess::AlongStepDoIt(aTrack, aStep);
291 
292  G4double MeanNumPhotons = GetAverageNumberOfPhotons(aParticle, aMaterial,
293  Rindex);
294  if (MeanNumPhotons <= 0.0) {
295  aParticleChange.SetNumberOfSecondaries(0);
296  return G4VContinuousProcess::AlongStepDoIt(aTrack, aStep);
297  }
298 
299  MeanNumPhotons *= aStep.GetStepLength();
300 
301  G4int NumPhotons = static_cast<G4int>(G4Poisson(MeanNumPhotons));
302 
303  // we don't want to produce secondaries within Geant4, since they will
304  // be handled within this dedicated Cerenkov routine for Auger.
305  aParticleChange.SetNumberOfSecondaries(0);
306  if (NumPhotons<=0)
307  return G4VContinuousProcess::AlongStepDoIt(aTrack, aStep);
308 
309 #if (G4VERSION_NUMBER < 920)
310  const G4double Pmin = Rindex->GetMinPhotonMomentum();
311  const G4double Pmax = Rindex->GetMaxPhotonMomentum();
312 #else
313  const G4double Pmin = Rindex->GetMinPhotonEnergy();
314  const G4double Pmax = Rindex->GetMaxPhotonEnergy();
315 #endif
316 
317  const G4double dp = Pmax - Pmin;
318  const G4double nMax = Rindex->GetMaxProperty();
319 
320  const G4double betaInverse = aParticle->GetTotalEnergy() /
321  aParticle->GetTotalMomentum();
322 
323  const G4double maxCos = betaInverse / nMax;
324  const G4double maxSin2 = (1.0 - (maxCos * maxCos));
325 
326  const double minQEMomentum = fQE[0].X();
327  const double maxQEMomentum = fQE[fQE.GetNPoints() - 1].X();
328 
329  // leave in stuff for dealing with change of refractive index with
330  // wavelength - even if the current setup ignores dispersion in water...
331 
332  // variables used in the loop later on
333  G4double rand, cosTheta, sin2Theta;
334  G4double phi, sinTheta, px, py, pz;
335  G4double invSumMFP;
336  G4double delta, deltaTime;
337  G4double sinThetaSinPhi, sinThetaCosPhi;
338  G4double radiusTest;
339  double absorptionMFP;
340  double rayleighMFP;
341  for (G4int i = 0; i < NumPhotons; ++i) {
342  do {
343  rand = G4UniformRand();
344  fSampledMomentum = Pmin + rand * dp;
345  fSampledRI = Rindex->GetProperty(fSampledMomentum);
346  cosTheta = betaInverse / fSampledRI;
347  sin2Theta = (1.0 - (cosTheta * cosTheta));
348  rand = G4UniformRand();
349  } while ((rand * maxSin2) > sin2Theta);
350 
351  // Kill according to quantum efficiency of PMT and collection efficiency
352 
353  if (fSampledMomentum < minQEMomentum ||
354  fSampledMomentum > maxQEMomentum)
355  continue;
356 
357  if (G4UniformRand() > fQE.Y(fSampledMomentum))
358  continue; // Photon is ignored
359 
360  // If we get here, we want to track the photon.
361  G4double SampledRI_Sq, RIConst;
362 
363  // calculate absorption and Rayleigh scattering constants for this photon
364  int iFirstValidBin_Wat = 0;
365  int iLastValidBin_Wat = fWaterAbsLength.GetNPoints() - 1;
366 
367  // If wrong... ignore it
368  if(fSampledMomentum> fWaterAbsLength[iFirstValidBin_Wat].X() &&
369  fSampledMomentum< fWaterAbsLength[iLastValidBin_Wat].X())
370  absorptionMFP = fWaterAbsLength.Y(fSampledMomentum); // CHECK
371  else continue;
372 
373 
374  SampledRI_Sq = fSampledRI * fSampledRI;
375  RIConst = (SampledRI_Sq + 2.0) * (SampledRI_Sq - 1.0);
376 
377  //does this need to be member function?
378  rayleighMFP = fRayleighConst / (pow(RIConst, 2) * pow(fSampledMomentum, 4));
379 
380  invSumMFP = 1.0 / (absorptionMFP + rayleighMFP);
381 
382  fInvMFP = (1.0 / absorptionMFP) + (1.0 / rayleighMFP);
383  fRayleighFrac = absorptionMFP * invSumMFP;
384  fAbsorbFrac = rayleighMFP * invSumMFP;
385 
386  // calculate photon vectors (direction, polarisation)
387  sinTheta = sqrt(sin2Theta);
388  rand = G4UniformRand();
389 
390  phi = 2.0 * utl::kPi * rand;
391  sinThetaSinPhi = sin(phi) * sinTheta;
392  sinThetaCosPhi = cos(phi) * sinTheta;
393 
394  px = (cosAlpha * cosBeta * sinThetaCosPhi) - (sinBeta * sinThetaSinPhi)
395  + (cosBeta * sinAlpha * cosTheta);
396  py = (cosAlpha * sinBeta * sinThetaCosPhi) + (cosBeta * sinThetaSinPhi)
397  + (sinBeta * sinAlpha * cosTheta);
398  pz = (cosAlpha * cosTheta) - (sinAlpha * sinThetaCosPhi);
399 
400  fPhotonMomentum.set(px, py, pz); // This is a momentum direction
401  fPhotonPolarization = (aPrimaryDirection - (cosTheta * fPhotonMomentum));
403  fPhotonIsFromMuonDecay=IsFromMuonDecay;
404 
405  rand = G4UniformRand();
406  delta = rand * aStep.GetStepLength();
407  deltaTime = delta / ((pPreStepPoint->GetVelocity() +
408  pPostStepPoint->GetVelocity()) / 2.0);
409  fPhotonTime = t0 + deltaTime;
410  fPhotonPosition = x0 + rand * aStep.GetDeltaPosition();
411 
412  radiusTest = fPhotonPosition.x() * fPhotonPosition.x() +
414 
415  // if Cerenkov photon is inside tank, then track it
416  if ((radiusTest < 0.0) && (fPhotonPosition.z() > fTankThickness) &&
419  }// for photons
420 
421  if (verboseLevel > 0) {
422  G4cerr << "\n Exiting from G4Cerenkov::DoIt -- NumberOfSecondaries = "
423  << aParticleChange.GetNumberOfSecondaries() << G4endl;
424  }
425 
426  return G4VContinuousProcess::AlongStepDoIt(aTrack, aStep);
427 
428 
429 }
430 
432 
433  if (fThePhysicsTable)
434  return;
435 
436 
437 
438  const G4MaterialTable* theMaterialTable= G4Material::GetMaterialTable();
439  G4int numOfMaterials = G4Material::GetNumberOfMaterials();
440  fThePhysicsTable = new G4PhysicsTable(numOfMaterials);
441 
442  G4PhysicsOrderedFreeVector *aPhysicsOrderedFreeVector;
443  G4Material *aMaterial;
444  G4MaterialPropertiesTable *aMaterialPropertiesTable;
445  G4MaterialPropertyVector *theRIndexVector;
446  G4double currentRI, currentPM, currentCAI;
447  G4double prevRI, prevPM, prevCAI;
448 
449  for (G4int i = 0; i < numOfMaterials; ++i) {
450 
451  aPhysicsOrderedFreeVector = new G4PhysicsOrderedFreeVector();
452  aMaterial = (*theMaterialTable)[i];
453  aMaterialPropertiesTable = aMaterial->GetMaterialPropertiesTable();
454 
455  if (aMaterialPropertiesTable) {
456 
457  theRIndexVector = aMaterialPropertiesTable->GetProperty("RINDEX");
458 
459  if (theRIndexVector) {
460 
461  theRIndexVector->ResetIterator();
462  ++(*theRIndexVector);
463  currentRI = theRIndexVector->GetProperty();
464 
465  if (currentRI > 1.0) {
466 
467 #if (G4VERSION_NUMBER < 920)
468  currentPM = theRIndexVector->GetPhotonMomentum();
469 #else
470  currentPM = theRIndexVector->GetPhotonEnergy();
471 #endif
472 
473  currentCAI = 0.0;
474  aPhysicsOrderedFreeVector->InsertValues(currentPM , currentCAI);
475  prevRI = currentRI;
476  prevPM = currentPM;
477  prevCAI = currentCAI;
478 
479  while(++(*theRIndexVector)) {
480 
481  currentRI = theRIndexVector->GetProperty();
482 
483 #if (G4VERSION_NUMBER < 920)
484  currentPM = theRIndexVector->GetPhotonMomentum();
485 #else
486  currentPM = theRIndexVector->GetPhotonEnergy();
487 #endif
488 
489  currentCAI = 0.5 * ((1.0 / (prevRI * prevRI)) +
490  (1.0 / (currentRI * currentRI)));
491  currentCAI = prevCAI + ((currentPM - prevPM) * currentCAI);
492  aPhysicsOrderedFreeVector->InsertValues(currentPM, currentCAI);
493  prevPM = currentPM;
494  prevCAI = currentCAI;
495  prevRI = currentRI;
496 
497  }
498 
499  }
500 
501  }
502 
503  }
504 
505  fThePhysicsTable->insertAt(i, aPhysicsOrderedFreeVector);
506 
507  }
508 
509 }
510 
511 G4double
513  G4double, G4double, G4double &) {
514 
515  if (fMaxPhotons <= 0)
516  return DBL_MAX;
517 
518  const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle();
519  const G4Material* aMaterial = aTrack.GetMaterial();
520 
521  G4MaterialPropertiesTable* aMaterialPropertiesTable =
522  aMaterial->GetMaterialPropertiesTable();
523 
524  if (!aMaterialPropertiesTable)
525  return DBL_MAX;
526 
527  const G4MaterialPropertyVector* Rindex =
528  aMaterialPropertiesTable->GetProperty("RINDEX");
529 
530  if (!Rindex)
531  return DBL_MAX;
532 
533  G4double meanNumPhotons = GetAverageNumberOfPhotons(aParticle, aMaterial,
534  Rindex);
535  if (meanNumPhotons <= 0.0)
536  return DBL_MAX;
537 
538  G4double StepLimit = fMaxPhotons / meanNumPhotons;
539  return StepLimit;
540 
541 }
542 
543 G4double
544 G4TankFastCerenkov::GetAverageNumberOfPhotons(const G4DynamicParticle* aParticle,
545  const G4Material* aMaterial,
546  const G4MaterialPropertyVector* Rindex) const {
547  const G4double Rfact = 369.81 / (eV * cm);
548 
549  if (aParticle->GetTotalMomentum() <= 0.0)
550  return 0.0;
551 
552  G4double betaInverse = aParticle->GetTotalEnergy() /
553  aParticle->GetTotalMomentum();
554 
555  G4int materialIndex = aMaterial->GetIndex();
556 
557  G4PhysicsOrderedFreeVector* CerenkovAngleIntegrals =
558  (G4PhysicsOrderedFreeVector*)((*fThePhysicsTable)(materialIndex));
559 
560  if (!(CerenkovAngleIntegrals->IsFilledVectorExist()))
561  return 0.0;
562 
563 #if (G4VERSION_NUMBER < 920)
564  G4double Pmin = Rindex->GetMinPhotonMomentum();
565  G4double Pmax = Rindex->GetMaxPhotonMomentum();
566 #else
567  G4double Pmin = Rindex->GetMinPhotonEnergy();
568  G4double Pmax = Rindex->GetMaxPhotonEnergy();
569 #endif
570 
571  G4double nMin = Rindex->GetMinProperty();
572  G4double nMax = Rindex->GetMaxProperty();
573  G4double CAImax = CerenkovAngleIntegrals->GetMaxValue();
574  G4double dp, ge;
575 
576  if (nMax < betaInverse) {
577 
578  dp = 0.0;
579  ge = 0.0;
580 
581  } else if (nMin > betaInverse) {
582 
583  dp = Pmax - Pmin;
584  ge = CAImax;
585 
586  } else {
587 
588 #if (G4VERSION_NUMBER < 920)
589  Pmin = Rindex->GetPhotonMomentum(betaInverse);
590 #else
591  Pmin = Rindex->GetPhotonEnergy(betaInverse);
592 #endif
593  dp = Pmax - Pmin;
594  G4bool isOutRange;
595  G4double CAImin = CerenkovAngleIntegrals->GetValue(Pmin, isOutRange);
596  ge = CAImax - CAImin;
597 
598  if (verboseLevel>0) {
599  G4cerr << "CAImin = " << CAImin << G4endl;
600  G4cerr << "ge = " << ge << G4endl;
601  }
602 
603  }
604 
605  G4double charge = aParticle->GetDefinition()->GetPDGCharge();
606  G4double numPhotons = Rfact * pow((charge / eplus), 2) *
607  (dp - ge * betaInverse*betaInverse);
608 
609  return numPhotons;
610 
611 }
612 
613 
614 //#include "Propagate.icc" // Contains the code for propogating photons -
615 // part of the same class and namespace, but put in a different file
616 // for clarity
617 
619 
620  const G4double rand = 2.0 * G4UniformRand() - 1.0;
621  const G4double cosTheta= (rand < 0.0)?
622  (-pow(-rand, 1.0 / 3.0)): pow(rand, 1.0 / 3.0);
623  const G4double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
624  const G4double phi = 2.0 * utl::kPi * G4UniformRand();
625  const G4double cosPhi = cos(phi);
626  const G4double sinPhi = sin(phi);
627  const G4double a = fPhotonPolarization.x();
628  const G4double b = fPhotonPolarization.y();
629  const G4double c = fPhotonPolarization.z();
630  const G4double k = 1.0 / sqrt(1.0 - c * c);
631  const G4double R11 = k * b;
632  const G4double R12 = k * a * c;
633  const G4double R21 = -k * a;
634  const G4double R22 = k * b * c;
635  const G4double R32 = -1.0 / k;
636 
637  // cerr << "Matrix\n";
638  // cerr << R11 << "\t" << R12 << "\t" << a << endl;
639  // cerr << R21 << "\t" << R22 << "\t" << b << endl;
640  // cerr << "0.000" << "\t" << R32 << "\t" << c << endl;
641  G4double x, y, z;
642 
643  if (G4UniformRand() < 0.5) {
644 
645  x = sinTheta * cosPhi;
646  y = sinTheta * sinPhi;
647  z = cosTheta;
648  fPhotonPolarization.set((R11 * x + R12 * y + a * z),
649  (R21 * x + R22 * y + b * z),
650  (R32 * y + c * z));
651  x = cosTheta * cosPhi;
652  y = cosTheta * sinPhi;
653  z = -sinTheta;
654  fPhotonMomentum.set((R11 * x + R12 * y + a * z),
655  (R21 * x + R22 * y + b * z),
656  (R32 * y + c * z));
657  // cerr << "Rayleigh1 " << x << " " << y << " " << z << endl;
658  } else {
659 
660  x = sinTheta * cosPhi;
661  y = sinTheta * sinPhi;
662  z = cosTheta;
663  fPhotonPolarization.set((R11 * x + R12 * y + a * z),
664  (R21 * x + R22 * y + b * z),
665  (R32 * y + c * z));
666  x = -cosTheta * cosPhi;
667  y = -cosTheta * sinPhi;
668  z = sinTheta;
669  // cerr << "Rayleigh2 " << x << " " << y << " " << z << endl;
670  fPhotonMomentum.set((R11 * x + R12 * y + a * z),
671  (R21 * x + R22 * y + b * z),
672  (R32 * y + c * z));
673  }
674 
675 }
676 
677 // included into the G4TankFastCerenkov class for handling cerenkov photons
678 // in the Geant4 Auger tank simulation
679 
681 {
682 
683  // This routine makes use of knowledge about the design of the Auger
684  // tanks that it shouldn't have (numbers not obtained through official
685  // routes, but hard-coded). That's life, if you want to make a program
686  // run faster.
687 
688  // The actual tracking is done in PropogateInTank and other routines -
689  // this one just determines which volume we start in, and gets us to
690  // the correct starting point in the nested methods. Once we return
691  // to this routine we fall straight back down to the level below,
692  // and move on to the next photon. Any detection of the photon has to
693  // done in the routines called from this one.
694 
695  if ((fPhotonMomentum.z() < 0.0) &&
698  return;
699  }
700 
701  G4double closest;
702  G4ThreeVector displacement;
703 
704  //loop over the PMTs and return whenever a PMT is hit
705  for(unsigned int i=1; i<4; ++i){
706  displacement = fPhotonPosition - fPMTPos[i];
707  closest = (displacement.x()*displacement.x()+displacement.y()*displacement.y())/fDomeRadiusSq +
708  (displacement.z()*displacement.z())/fDomeRadiuszSq;
709  if (closest < 1. ) {
710  closest = (displacement.x()*displacement.x()+displacement.y()*displacement.y())/fInterfaceRadiusSq +
711  (displacement.z()*displacement.z())/fInterfaceRadiuszSq;
712  if (closest < 1. ) {
713  closest = (displacement.x()*displacement.x()+displacement.y()*displacement.y())/fPMTRadiusSq +
714  (displacement.z()*displacement.z())/fPMTRadiuszSq;
715  if (closest < 1. && -displacement.z() > fHeightz ) { // z < fHeightz : insensitive area
717  return;
718  }
720  return;
721  }
723  return;
724  }
725  }
726 
728 
729  return;
730 }
731 
733 {
734  G4bool Dead;
735  if (flag > DOWN_IN_TANK) {
736  if ((flag == IN_DOME_1) || (flag == IN_INTERFACE_1)) {
737  Dead = TransitionToDome<1>(flag);
738  if (Dead) return;
739  }
740  else if ((flag == IN_DOME_2) || (flag == IN_INTERFACE_2)) {
741  // G4cerr << "IN_DOME_2 or IN_INTERFACE_2" << G4endl;
742  Dead = TransitionToDome<2>(flag);
743  if (Dead) return;
744  }
745  else { // IN_DOME_3 or IN_INTERFACE_3
746  // G4cerr << "IN_DOME_3 or IN_INTERFACE_3" << G4endl;
747  Dead = TransitionToDome<3>(flag);
748  if (Dead) return;
749  }
750  if (fPhotonMomentum.z() < -0.0463) flag = DOWN_IN_TANK;
751  // A photon emerging anywhere from a dome with z component of
752  // direction below this cannot possibly hit another dome
753  }
754 
755  G4double DistanceToFloor, DistanceToWall, DistanceTravelled, Distance;
756  G4double DistanceToRoof, DistanceToPMT1, DistanceToPMT2, DistanceToPMT3;
757  G4double alpha, beta, gamma;
758  G4double TestVal;
759  G4bool repeat;
760  G4double closest1, closest2, closest3;
761  G4ThreeVector closest, Displacement1, Displacement2, Displacement3;
762  G4double DotProduct, rand, DistanceToPMT;
763  G4int HitPMT, TargetPMT = -1, Target;
764 
765  do {
766  repeat = false;
767  if (flag == DOWN_IN_TANK) {
768  // photon cannot hit domes, so only check for base and wall
769 
770  if(!fPhotonMomentum.z()){
771  WARNING("The z component of the momentul is 0, skipping photon.");
772  return;
773  }
774 
775  DistanceToFloor = -(fPhotonPosition.z()-fTankThickness) / fPhotonMomentum.z();
776 
777  gamma = 1.0 / (fPhotonMomentum.x() * fPhotonMomentum.x() +
778  fPhotonMomentum.y() * fPhotonMomentum.y());
779  alpha = (fPhotonMomentum.x() * fPhotonPosition.x() +
780  fPhotonMomentum.y() * fPhotonPosition.y()) * gamma;
781  beta = (fPhotonPosition.x() * fPhotonPosition.x() +
782  fPhotonPosition.y() * fPhotonPosition.y() -
783  fTankRadiusSq) * gamma;
784 
785  const double distance=alpha * alpha - beta;
786  if( distance<0){
787  WARNING("Distance to wall is not finite, skipping photon");
788  return;
789  }
790 
791  DistanceToWall = sqrt(distance) - alpha;
792 
793  // check for Rayleigh scattering or absorption en route
794  rand = G4UniformRand();
795  TestVal=(DistanceToWall < DistanceToFloor)?
796  (1.0 - exp(-DistanceToWall * fInvMFP)):(1.0 - exp(-DistanceToFloor * fInvMFP));
797 
798  if (rand < TestVal) {
799  // some interaction has occurred before reaching destination
800  if (rand < (TestVal * fRayleighFrac)) {
801  // we have a Rayleigh scatter
802  DistanceTravelled = -log(1.0 - (rand / fRayleighFrac)) / fInvMFP;
803  fPhotonPosition += DistanceTravelled * fPhotonMomentum;
804  fPhotonTime += DistanceTravelled * fSampledRI / c_light;
805  DoRayleighScatter(); // to pick new polarisation and direction
806  if ((fPhotonMomentum.z() < 0.0) &&
808  flag = DOWN_IN_TANK;
809  else flag = IN_TANK;
810  repeat = true;
811  }
812  else
813  return; // we have absorption
814  }
815  if (!repeat) {
816  // no absorption or scattering - we reach the destination surface
817  if (DistanceToWall < DistanceToFloor) {
818  // we hit the wall
819  fPhotonPosition += DistanceToWall * fPhotonMomentum;
820  fPhotonTime += DistanceToWall * fSampledRI / c_light;
821  Dead = ScatterOffWall();
822  }
823  else {
824  // we hit the floor
825  fPhotonPosition += DistanceToFloor * fPhotonMomentum;
826  fPhotonTime += DistanceToFloor * fSampledRI / c_light;
827  Dead = ScatterOffFloor();
828  }
829  if (Dead) return;
830  repeat = true;
831  }
832  }
833  else {
834  HitPMT = 0;
835  if (fPhotonMomentum.z() > 0)
836  DistanceToRoof = (fTankHeight + fTankThickness - fPhotonPosition.z()) /
837  fPhotonMomentum.z();
838  else DistanceToRoof = -(fPhotonPosition.z()-fTankThickness) / fPhotonMomentum.z();// floor
839 
840  gamma = 1.0 / (fPhotonMomentum.x() * fPhotonMomentum.x() +
841  fPhotonMomentum.y() * fPhotonMomentum.y());
842  alpha = (fPhotonMomentum.x() * fPhotonPosition.x() +
843  fPhotonMomentum.y() * fPhotonPosition.y()) * gamma;
844  beta = (fPhotonPosition.x() * fPhotonPosition.x() +
845  fPhotonPosition.y() * fPhotonPosition.y() -
846  fTankRadiusSq) * gamma;
847  DistanceToWall = sqrt(alpha * alpha - beta) - alpha;
848 
849  Displacement1 = fPhotonPosition - fPMTPos[1];
850  DotProduct = Displacement1.dot(fPhotonMomentum);
851  closest = Displacement1 - (DotProduct * fPhotonMomentum);
852  closest1 = (closest.x()*closest.x()+closest.y()*closest.y())/fDomeRadiusSq +
853  (closest.z()*closest.z())/fDomeRadiuszSq;
854  if (closest1 < 1. ) HitPMT += 1;
855 
856  Displacement2 = fPhotonPosition - fPMTPos[2];
857  DotProduct = Displacement2.dot(fPhotonMomentum);
858  closest = Displacement2 - (DotProduct * fPhotonMomentum);
859  closest2 = (closest.x()*closest.x()+closest.y()*closest.y())/fDomeRadiusSq +
860  (closest.z()*closest.z())/fDomeRadiuszSq;
861  if (closest2 < 1. ) HitPMT += 2;
862 
863  Displacement3 = fPhotonPosition - fPMTPos[3];
864  DotProduct = Displacement3.dot(fPhotonMomentum);
865  closest = Displacement3 - (DotProduct * fPhotonMomentum);
866  closest3 = (closest.x()*closest.x()+closest.y()*closest.y())/fDomeRadiusSq +
867  (closest.z()*closest.z())/fDomeRadiuszSq;
868  if (closest3 < 1. ) HitPMT += 4;
869 
870  switch (HitPMT) {
871  case 0 :
872  DistanceToPMT = 1.0e99;
873  break;
874  case 1 :
875  DistanceToPMT = GetEllipsoidIntersect(Displacement1, fDomeRadius, fDomeRadiusz);
876  TargetPMT = TARGET_PMT1;
877  break;
878  case 2 :
879  DistanceToPMT = GetEllipsoidIntersect(Displacement2, fDomeRadius, fDomeRadiusz);
880  TargetPMT = TARGET_PMT2;
881  break;
882  case 3 :
883  DistanceToPMT1 = GetEllipsoidIntersect(Displacement1, fDomeRadius, fDomeRadiusz);
884  DistanceToPMT2 = GetEllipsoidIntersect(Displacement2, fDomeRadius, fDomeRadiusz);
885  if (DistanceToPMT1 < DistanceToPMT2) {
886  TargetPMT = TARGET_PMT1;
887  DistanceToPMT = DistanceToPMT1;
888  }
889  else {
890  TargetPMT = TARGET_PMT2;
891  DistanceToPMT = DistanceToPMT2;
892  }
893  break;
894  case 4 :
895  DistanceToPMT = GetEllipsoidIntersect(Displacement3, fDomeRadius, fDomeRadiusz);
896  TargetPMT = TARGET_PMT3;
897  break;
898  case 5 :
899  DistanceToPMT1 = GetEllipsoidIntersect(Displacement1, fDomeRadius, fDomeRadiusz);
900  DistanceToPMT3 = GetEllipsoidIntersect(Displacement3, fDomeRadius, fDomeRadiusz);
901  if (DistanceToPMT1 < DistanceToPMT3) {
902  TargetPMT = TARGET_PMT1;
903  DistanceToPMT = DistanceToPMT1;
904  }
905  else {
906  TargetPMT = TARGET_PMT3;
907  DistanceToPMT = DistanceToPMT3;
908  }
909  break;
910  case 6 :
911  DistanceToPMT2 = GetEllipsoidIntersect(Displacement2, fDomeRadius, fDomeRadiusz);
912  DistanceToPMT3 = GetEllipsoidIntersect(Displacement3, fDomeRadius, fDomeRadiusz);
913  if (DistanceToPMT2 < DistanceToPMT3) {
914  TargetPMT = TARGET_PMT2;
915  DistanceToPMT = DistanceToPMT2;
916  }
917  else {
918  TargetPMT = TARGET_PMT3;;
919  DistanceToPMT = DistanceToPMT3;
920  }
921  break;
922  default :
923  // hit all 3 pmts - error
924  WARNING("Error determining next photon intersection in PropagateInTank()");
925  return;
926  break;
927  //cerr << "Error determining next intersection of photon in" << endl;
928  //cerr << "G4TankFastCerenkov::PropogateInTank()" << endl;
929  //exit(-1);
930  }
931 
932  if (DistanceToPMT < DistanceToWall) {
933  if (DistanceToPMT < DistanceToRoof) {
934  Target = TargetPMT;
935  Distance = DistanceToPMT;
936  }
937  else {
938  Target = TARGET_FLOOR;
939  Distance = DistanceToRoof;
940  }
941  }
942  else {
943  if (DistanceToRoof < DistanceToWall) {
944  Target = TARGET_FLOOR;
945  Distance = DistanceToRoof;
946  }
947  else {
948  Target = TARGET_WALL;
949  Distance = DistanceToWall;
950  }
951  }
952  // check for Rayleigh scattering or absorption en route
953  rand = G4UniformRand();
954  TestVal = 1.0 - exp(-Distance * fInvMFP);
955  if (rand < TestVal) {
956  // some interaction has occurred before reaching destination
957  if (rand >= (TestVal * fRayleighFrac)) {
958  return; // absorption
959  }
960  // if we get here we have a Rayleigh scatter
961  DistanceTravelled = -log(1.0 - (rand / fRayleighFrac)) / fInvMFP;
962  fPhotonPosition += DistanceTravelled * fPhotonMomentum;
963  fPhotonTime += DistanceTravelled * fSampledRI / c_light;
964  DoRayleighScatter(); // to pick new polarisation and direction
965  if ((fPhotonMomentum.z() < 0.0) &&
967  flag = DOWN_IN_TANK;
968  else flag = IN_TANK;
969  repeat = true;
970  }
971  else {
972  fPhotonPosition += Distance * fPhotonMomentum;
973  fPhotonTime += Distance * fSampledRI / c_light;
974  switch (Target) {
975  case TARGET_WALL :
976  Dead = ScatterOffWall();
977  if (Dead) {
978  return;
979  }
980  break;
981  case TARGET_FLOOR :
982  if (fPhotonMomentum.z() < 0) Dead = ScatterOffFloor();
983  else Dead = ScatterOffRoof();
984  if (Dead) return;
985  break;
986  case TARGET_PMT1 :
987  Dead = TransitionToDome<1>(INWARD);
988  if (Dead) return;
989  break;
990  case TARGET_PMT2 :
991  Dead = TransitionToDome<2>(INWARD);
992  if (Dead) return;
993  break;
994  case TARGET_PMT3 :
995  Dead = TransitionToDome<3>(INWARD);
996  if (Dead) return;
997  break;
998  default:
999  WARNING("Error finding target in PropagateInTank");
1000  return;
1001 
1002  }
1003  repeat = true;
1004  }
1005  }
1006 
1007  // quick check if we can ignore PMTs on next track
1009  (fPhotonMomentum.z() < 0.0))
1010  flag = DOWN_IN_TANK;
1011  else
1012  flag = IN_TANK;
1013 
1014  } while (repeat);
1015 
1016  // never get here - we either get detected or absorbed inside the loop
1017  std::string err("Tank photon tracking ran into an impossible condition");
1018  ERROR(err);
1019  throw utl::OutOfBoundException(err);
1020 }
1021 
1022 
1023 // *************
1024 // PMT ROUTINES
1025 // *************
1026 
1027 template<int pmtId>
1029 {
1030  fPhotonPosition -= fPMTPos[pmtId];
1031  G4ThreeVector Alpha, Beta, Delta, Normal;
1032  G4bool reflected, Dead/*, NormalFlag*/;
1033  G4double n, n2, Transmission, VDotN;
1034  G4double ParaComp, PerpComp, ParaMag, PerpMag, X;
1035  G4ThreeVector ParaPolarization, PerpPolarization;
1036 
1037  if ( (flag == IN_INTERFACE_1) || (flag == IN_DOME_1) ||
1038  (flag == IN_INTERFACE_2) || (flag == IN_DOME_2) ||
1039  (flag == IN_INTERFACE_3) || (flag == IN_DOME_3) ) {
1040  Dead = PropogateInDome<pmtId>(flag); // flag needs to be altered in PiD
1041  if (Dead) return true;
1042  }
1043 
1044  do {
1045  if (flag == INWARD) {
1046  n = fSampledRI;
1048  }
1049  else {
1051  n2 = fSampledRI;
1052  }
1053  Normal.setX(2.*fPhotonPosition.x()/fDomeRadiusSq);
1054  Normal.setY(2.*fPhotonPosition.y()/fDomeRadiusSq);
1055  Normal.setZ(2.*fPhotonPosition.z()/fDomeRadiuszSq);
1056  Normal /= Normal.mag();
1057  VDotN = -Normal.dot(fPhotonMomentum); // is actually cos(theta)
1058  if (VDotN > 0.999999) {
1059  Transmission = 4.0 * n * n2 / pow((n + n2), 2);
1060  reflected = G4UniformRand() > Transmission;
1061  if (reflected) {
1064  if (flag == INWARD) {
1065  fPhotonPosition += fPMTPos[pmtId];
1066  return false;
1067  }
1068  flag = INWARD;
1069  Dead = PropogateInDome<pmtId>(flag);
1070  if (Dead) return true;
1071  }
1072  else {
1073  if (flag == OUTWARD) {
1074  fPhotonPosition += fPMTPos[pmtId];
1075  return false;
1076  }
1077  Dead = PropogateInDome<pmtId>(flag);
1078  if (Dead) return true;
1079  }
1080  }
1081  else {
1082  Alpha = Normal.cross(fPhotonMomentum);
1083  Alpha /= Alpha.mag();
1084  X = (n2 * n2) - (n * n) * (1.0 - (VDotN * VDotN));
1085  if (X <= 0.0) X = 0.0; // total reflection
1086  else {
1087  if (VDotN > 0.0) X = -sqrt(X);
1088  else X = sqrt(X);
1089  }
1090  ParaMag = fPhotonPolarization.dot(Alpha);
1091  Beta = fPhotonMomentum.cross(Alpha); // should be unit vector
1092  PerpMag = fPhotonPolarization.dot(Beta);
1093  PerpComp = PerpMag / (n * VDotN - X);
1094  ParaComp = ParaMag / (n2 * VDotN - (n * X / n2));
1095  Transmission = -(4.0 * n * VDotN * X) * (PerpComp * PerpComp +
1096  ParaComp * ParaComp);
1097  reflected = (G4UniformRand() >= Transmission);
1098  if (X == 0.0) reflected = true;
1099  if (flag == INWARD) {
1100  if (reflected) {
1101  // reflected back into tank
1102  fPhotonMomentum += 2.0 * VDotN * Normal;
1104  Beta = fPhotonMomentum.cross(Alpha);
1105  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1107  fPhotonPosition *= 1.00001; // Fudge to avoid rounding errors
1108  fPhotonPosition += fPMTPos[pmtId];
1109  return false; // so we continue tracking in Tank
1110  }
1111  else {
1112  // transmitted in to dome
1113  fPhotonMomentum *= n;
1114  fPhotonMomentum += ((n * VDotN) + X) * Normal;
1116  Beta = fPhotonMomentum.cross(Alpha);
1117  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1119  Dead = PropogateInDome<pmtId>(flag); // flag needs to be altered in PiD
1120  if (Dead) return true;
1121  }
1122  }
1123  else {
1124  // reflected off inside of dome - remains in dome
1125  if (reflected) {
1126  fPhotonMomentum += 2.0 * VDotN * Normal;
1128  Beta = fPhotonMomentum.cross(Alpha);
1129  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1131  flag = INWARD;
1132  Dead = PropogateInDome<pmtId>(flag);
1133  if (Dead) return true;
1134  }
1135  else {
1136  // transmitted from dome back into tank
1137  fPhotonMomentum *= n;
1138  fPhotonMomentum += ((n * VDotN) + X) * Normal;
1140  Beta = fPhotonMomentum.cross(Alpha);
1141  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1143  fPhotonPosition *= 1.00001; // Fudge to avoid rounding errors
1144  fPhotonPosition += fPMTPos[pmtId];
1145  return false;
1146  }
1147  }
1148  }
1149  } while (true);
1150 }
1151 
1152 template<int pmtId>
1154 {
1155  G4bool Dead;
1156  if (flag == IN_INTERFACE_1 ||
1157  flag == IN_INTERFACE_2 ||
1158  flag == IN_INTERFACE_3
1159  ) {
1160  Dead = TransitionToInterface<pmtId>(flag);
1161  if (Dead) return true;
1162  }
1163 
1164  G4double DistanceThis, DistanceOther, DistanceToRoof;
1165  // we start at one dome surface, so only one non-zero solution exists
1166  do {
1167  DistanceToRoof = (fRoofPos[pmtId] - fPhotonPosition.z()) / fPhotonMomentum.z();
1168  if (DistanceToRoof < 0.0) DistanceToRoof = 1.0e99;
1169  if (flag == INWARD) {
1170  // DistanceThis = -2.0 * fPhotonPosition.dot(fPhotonMomentum);
1173  if (DistanceThis < DistanceOther) {
1174  if (DistanceToRoof < DistanceThis) return true; // photon killed
1175  Dead = (G4UniformRand() > exp(-DistanceThis /
1177  if (Dead) return true;
1178  fPhotonPosition += DistanceThis * fPhotonMomentum;
1179  fPhotonTime += DistanceThis * fDomeRIndex.Y(fSampledMomentum) / c_light;
1180  flag = OUTWARD;
1181  return false;
1182  }
1183  else {
1184  if (DistanceToRoof < DistanceOther) return true; // photon killed
1185  Dead = (G4UniformRand() > exp(-DistanceOther /
1187  if (Dead) return true;
1188  fPhotonPosition += DistanceOther * fPhotonMomentum;
1189  fPhotonTime += DistanceOther * fDomeRIndex.Y(fSampledMomentum) / c_light;
1190  Dead = TransitionToInterface<pmtId>(flag);
1191  if (Dead) return true;
1192  }
1193  }
1194  else {
1196  if (DistanceToRoof < DistanceOther) return true; // photon killed
1197  Dead = (G4UniformRand() > exp(-DistanceOther /
1199  if (Dead) return true;
1200  fPhotonPosition += DistanceOther * fPhotonMomentum;
1201  fPhotonTime += DistanceOther * fDomeRIndex.Y(fSampledMomentum) / c_light;
1202  flag = OUTWARD;
1203  return false;
1204  }
1205  } while (true);
1206 }
1207 
1208 template<int pmtId>
1210 {
1211  G4ThreeVector Alpha, Beta, Delta, Normal;
1212  G4bool reflected, Dead/*, NormalFlag*/;
1213  G4double n, n2, Transmission, VDotN;
1214  G4double ParaComp, PerpComp, ParaMag, PerpMag, X;
1215  G4ThreeVector ParaPolarization, PerpPolarization;
1216 
1217  if (flag == IN_INTERFACE_1 ||
1218  flag == IN_INTERFACE_2 ||
1219  flag == IN_INTERFACE_3
1220  ) {
1221  Dead = PropogateInInterface<pmtId>(flag); // flag needs to be altered in PiD
1222  if (Dead) return true;
1223  }
1224 
1225  do {
1226  if (flag == INWARD) {
1229  }
1230  else {
1233  }
1234  Normal.setX(2.*fPhotonPosition.x()/fInterfaceRadiusSq);
1235  Normal.setY(2.*fPhotonPosition.y()/fInterfaceRadiusSq);
1236  Normal.setZ(2.*fPhotonPosition.z()/fInterfaceRadiuszSq);
1237  Normal /= Normal.mag();
1238  VDotN = -Normal.dot(fPhotonMomentum); // is actually cos(theta)
1239  if (VDotN > 0.999999) {
1240  Transmission = 4.0 * n * n2 / pow((n + n2), 2);
1241  reflected = G4UniformRand() > Transmission;
1242  if (reflected) {
1245  if (flag == INWARD) {
1246  flag = OUTWARD;
1247  return false;
1248  }
1249  flag = INWARD;
1250  Dead = PropogateInInterface<pmtId>(flag);
1251  if (Dead) return true;
1252  }
1253  else {
1254  if (flag == OUTWARD) return true;
1255  Dead = PropogateInInterface<pmtId>(flag);
1256  if (Dead) return true;
1257  }
1258  }
1259  else {
1260  Alpha = Normal.cross(fPhotonMomentum);
1261  Alpha /= Alpha.mag();
1262  X = (n2 * n2) - (n * n) * (1.0 - (VDotN * VDotN));
1263  if (X <= 0.0) X = 0.0; // total reflection
1264  else {
1265  if (VDotN > 0.0) X = -sqrt(X);
1266  else X = sqrt(X);
1267  }
1268  ParaMag = fPhotonPolarization.dot(Alpha);
1269  Beta = fPhotonMomentum.cross(Alpha); // should be unit vector
1270  PerpMag = fPhotonPolarization.dot(Beta);
1271  PerpComp = PerpMag / (n * VDotN - X);
1272  ParaComp = ParaMag / (n2 * VDotN - (n * X / n2));
1273  Transmission = -(4.0 * n * VDotN * X) * (PerpComp * PerpComp +
1274  ParaComp * ParaComp);
1275  reflected = (G4UniformRand() >= Transmission);
1276  if (X == 0.0) reflected = true;
1277  if (flag == INWARD) {
1278  if (reflected) {
1279  // reflected back into dome
1280  fPhotonMomentum += 2.0 * VDotN * Normal;
1282  Beta = fPhotonMomentum.cross(Alpha);
1283  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1285  flag = OUTWARD;
1286  return false;
1287  }
1288  else {
1289  // transmitted back into tank
1290  fPhotonMomentum *= n;
1291  fPhotonMomentum += ((n * VDotN) + X) * Normal;
1293  Beta = fPhotonMomentum.cross(Alpha);
1294  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1296  Dead = PropogateInInterface<pmtId>(flag);
1297  if (Dead) return true;
1298  }
1299  }
1300  else {
1301  // reflected off inside of dome - remains in interface
1302  if (reflected) {
1303  fPhotonMomentum += 2.0 * VDotN * Normal;
1305  Beta = fPhotonMomentum.cross(Alpha);
1306  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1308  flag = INWARD;
1309  Dead = PropogateInInterface<pmtId>(flag);
1310  if (Dead) return true;
1311  }
1312  else {
1313  // transmitted from interface back into dome
1314  fPhotonMomentum *= n;
1315  fPhotonMomentum += ((n * VDotN) + X) * Normal;
1317  Beta = fPhotonMomentum.cross(Alpha);
1318  fPhotonPolarization = ParaComp * Alpha + PerpComp * Beta;
1320  return false;
1321  }
1322  }
1323  }
1324  } while (true);
1325 }
1326 
1327 template<int pmtId>
1329 {
1330  G4bool Dead;
1331  G4double DistanceThis, DistanceOther, DistanceToRoof;
1332  DistanceToRoof = (fRoofPos[pmtId] - fPhotonPosition.z()) / fPhotonMomentum.z();
1333  if (DistanceToRoof < 0.0) DistanceToRoof = 1.0e99;
1334 
1335  // we start at interface surface, so only one non-zero solution exists
1336  // DistanceThis = -2.0 * fPhotonPosition.dot(fPhotonMomentum);
1339 
1340  if (DistanceThis < DistanceOther) {
1341  if (DistanceToRoof < DistanceThis) return true; // photon killed
1342  Dead = (G4UniformRand() > exp(-DistanceThis /
1344  if (Dead) return true;
1345  fPhotonPosition += DistanceThis * fPhotonMomentum;
1346  fPhotonTime += DistanceThis * fInterfaceRIndex.Y(fSampledMomentum) / c_light;
1347  flag = OUTWARD;
1348  return false;
1349  }
1350  else {
1351  if (DistanceToRoof < DistanceOther) return true; // photon killed
1352  Dead = (G4UniformRand() > exp(-DistanceOther /
1354  if (Dead) return true;
1355  fPhotonTime += DistanceOther * fInterfaceRIndex.Y(fSampledMomentum) / c_light;
1356 
1357  if ( (-fPhotonPosition.z()) > fHeightz )
1359  return true; // photon absorbed, so is now dead
1360  }
1361 }
1362 
1363 // *******************
1364 // Scattering routines
1365 // *******************
1366 
1368 {
1369  G4ThreeVector Normal(0.0, 0.0, -1.0);
1370  G4double Reflectivity = fLinerReflectivity.Y(fSampledMomentum);
1371  G4double r = G4UniformRand();
1372  if (r > Reflectivity) return true; // absorbed
1373  r /= Reflectivity; // range 0..1
1374  G4double Lobe = fLinerSpecularLobe.Y(fSampledMomentum);
1375  if (r <= Lobe) {
1376  LobeScatterHorizontal(Normal);
1377  return false;
1378  }
1379  G4double Spike = Lobe + fLinerSpecularSpike.Y(fSampledMomentum);
1380  if (r <= Spike) {
1381  SpikeScatter(Normal);
1382  return false;
1383  }
1384  G4double Back = Spike + fLinerBackscatter.Y(fSampledMomentum);
1385  if (r <= Back) {
1386  BackScatter();
1387  return false;
1388  }
1389  DiffuseScatterHorizontal(Normal);
1390  return false;
1391 }
1392 
1394 {
1395  G4ThreeVector Normal;
1396  G4double Reflectivity = fLinerReflectivity.Y(fSampledMomentum);
1397  G4double r = G4UniformRand();
1398  if (r > Reflectivity) return true; // absorbed
1399  r /= Reflectivity; // range 0..1
1400  G4double X = fPhotonPosition.x();
1401  G4double Y = fPhotonPosition.y();
1402  G4double W = 1.0 / sqrt((X * X) + (Y * Y));
1403  Normal.set(-X * W, -Y * W, 0.0);
1404  G4double Lobe = fLinerSpecularLobe.Y(fSampledMomentum);
1405  if (r <= Lobe) {
1406  LobeScatterVertical(Normal);
1407  return false;
1408  }
1409  G4double Spike = Lobe + fLinerSpecularSpike.Y(fSampledMomentum);
1410  if (r <= Spike) {
1411  SpikeScatter(Normal);
1412  return false;
1413  }
1414  G4double Back = Spike + fLinerBackscatter.Y(fSampledMomentum);
1415  if (r <= Back) {
1416  BackScatter();
1417  return false;
1418  }
1419  DiffuseScatterVertical(Normal);
1420  return false;
1421 }
1422 
1424 {
1425  G4ThreeVector Normal(0.0, 0.0, 1.0);
1426  G4double Reflectivity = fLinerReflectivity.Y(fSampledMomentum);
1427  G4double r = G4UniformRand();
1428  if (r > Reflectivity) return true; // absorbed
1429  r /= Reflectivity; // range 0..1
1430  G4double Lobe = fLinerSpecularLobe.Y(fSampledMomentum);
1431  if (r <= Lobe) {
1432  LobeScatterHorizontal(Normal);
1433  return false;
1434  }
1435  G4double Spike = Lobe + fLinerSpecularSpike.Y(fSampledMomentum);
1436  if (r <= Spike) {
1437  SpikeScatter(Normal);
1438  return false;
1439  }
1440  G4double Back = Spike + fLinerBackscatter.Y(fSampledMomentum);
1441  if (r <= Back) {
1442  BackScatter();
1443  return false;
1444  }
1445  DiffuseScatterHorizontal(Normal);
1446  return false;
1447 }
1448 
1450 {
1452  -fPhotonMomentum.z());
1453  // can leave polarisation as it is
1454  return;
1455 }
1456 
1457 void G4TankFastCerenkov::DiffuseScatterVertical(const G4ThreeVector& Normal)
1458 {
1459  // for scattering off the tank walls
1460  G4double rand = G4UniformRand();
1461  G4double cosTheta = sqrt(1.0 - rand);
1462  G4double sinTheta = sqrt(rand);
1463  G4double Phi = 2.0 * utl::kPi * G4UniformRand();
1464  G4double sinThetaCosPhi = sinTheta * cos(Phi);
1465  G4double sinThetaSinPhi = sinTheta * sin(Phi);
1466  G4double X = Normal.x();
1467  G4double Y = Normal.y();
1468  G4double W = sqrt((X * X) + (Y * Y));
1469  G4double Vx = (X * cosTheta) - (Y * sinThetaSinPhi / W);
1470  G4double Vy = (Y * cosTheta) + (X * sinThetaSinPhi / W);
1471  G4double Vz = -W * sinThetaCosPhi;
1472  fPhotonMomentum.set(Vx, Vy, Vz);
1473  G4double DotProduct = fPhotonMomentum.dot(fPhotonPolarization);
1476  return;
1477 }
1478 
1479 void G4TankFastCerenkov::DiffuseScatterHorizontal(const G4ThreeVector& Normal)
1480 {
1481  // for scattering off tank floor or roof
1482  G4double rand = G4UniformRand();
1483  G4double cosTheta = sqrt(1.0 - rand);
1484  G4double sinTheta = sqrt(rand);
1485  G4double Phi = 2.0 * utl::kPi * G4UniformRand();
1486  G4double Vx = sinTheta * cos(Phi);
1487  G4double Vy = sinTheta * sin(Phi);
1488  G4double Vz = Normal.z() * cosTheta; // Normal.z() = +/- 1
1489  fPhotonMomentum.set(Vx, Vy, Vz);
1490  G4double DotProduct = fPhotonMomentum.dot(fPhotonPolarization);
1493  return;
1494 }
1495 
1496 void G4TankFastCerenkov::SpikeScatter(const G4ThreeVector& Normal)
1497 {
1498  G4double DotProduct = fPhotonMomentum.dot(Normal); // less than zero;
1499  fPhotonMomentum = fPhotonMomentum - (2.0 * DotProduct) * Normal;
1500  DotProduct = fPhotonMomentum.dot(fPhotonPolarization);
1502  fPhotonMomentum /= fPhotonMomentum.mag();
1504  return;
1505 }
1506 
1507 void G4TankFastCerenkov::LobeScatterVertical(const G4ThreeVector& Normal)
1508 {
1509  G4double Alpha, Phi;
1510  G4ThreeVector TrialMomentum;
1511  G4ThreeVector FacetNormal;
1512  G4double DotProduct;
1513 
1514  const G4double f_max = (fSigmaAlpha < 0.25) ? 4.0 * fSigmaAlpha : 1.0;
1515 
1516  G4double sinAlpha, cosAlpha, sinAlphaSinPhi, sinAlphaCosPhi;
1517  G4double W, X, Y, Vx, Vy, Vz;
1518  do {
1519  do {
1520  do {
1521  Alpha = G4RandGauss::shoot(0.0, fSigmaAlpha);
1522  sinAlpha = sin(Alpha);
1523  } while (((G4UniformRand()*f_max) > sinAlpha) || (Alpha >= (0.5 *utl::kPi)));
1524  Phi = 2.0 * utl::kPi * G4UniformRand();
1525  cosAlpha = cos(Alpha);
1526  sinAlphaSinPhi = sinAlpha * sin(Phi);
1527  sinAlphaCosPhi = sinAlpha * cos(Phi);
1528  X = Normal.x();
1529  Y = Normal.y();
1530  W = sqrt((X * X) + (Y * Y));
1531  Vx = (X * cosAlpha) - (Y * sinAlphaSinPhi / W);
1532  Vy = (Y * cosAlpha) + (X * sinAlphaSinPhi / W);
1533  Vz = - W * sinAlphaCosPhi;
1534  FacetNormal.set(Vx, Vy, Vz);
1535  DotProduct = fPhotonMomentum.dot(FacetNormal);
1536  } while (DotProduct >= 0.0);
1537  TrialMomentum = fPhotonMomentum - (2.0 * DotProduct) * FacetNormal;
1538  } while (TrialMomentum.dot(Normal) <= 0.0);
1539 
1540  fPhotonMomentum = TrialMomentum;
1541  DotProduct = fPhotonMomentum.dot(fPhotonPolarization);
1543  fPhotonMomentum /= fPhotonMomentum.mag();
1545  return;
1546 }
1547 
1548 void G4TankFastCerenkov::LobeScatterHorizontal(const G4ThreeVector& Normal)
1549 {
1550  G4double Alpha, Phi;
1551  G4ThreeVector TrialMomentum;
1552  G4ThreeVector FacetNormal;
1553  G4double DotProduct;
1554  G4double f_max = (fSigmaAlpha < 0.25) ? 4.0 * fSigmaAlpha : 1.0;
1555  G4double sinAlpha, cosAlpha/*, sinAlphaSinPhi, sinAlphaCosPhi*/;
1556  G4double /*W, X, Y, Z,*/ Vx, Vy, Vz;
1557  do {
1558  do {
1559  do {
1560  Alpha = G4RandGauss::shoot(0.0, fSigmaAlpha);
1561  sinAlpha = sin(Alpha);
1562  } while (((G4UniformRand()*f_max) > sinAlpha) || (Alpha >= (0.5 *utl::kPi)));
1563 
1564  Phi = 2.0 * utl::kPi * G4UniformRand();
1565  cosAlpha = cos(Alpha);
1566  Vx = sinAlpha * cos(Phi);
1567  Vy = sinAlpha * sin(Phi);
1568  Vz = Normal.z() * cosAlpha; // Normal.z() = +/- 1
1569  FacetNormal.set(Vx, Vy, Vz);
1570  DotProduct = fPhotonMomentum.dot(FacetNormal);
1571  } while (DotProduct >= 0.0);
1572 
1573  TrialMomentum = fPhotonMomentum - (2.0 * DotProduct) * FacetNormal;
1574  } while (TrialMomentum.dot(Normal) <= 0.0);
1575 
1576  fPhotonMomentum = TrialMomentum;
1577  DotProduct = fPhotonMomentum.dot(fPhotonPolarization);
1579  fPhotonMomentum /= fPhotonMomentum.mag();
1581  return;
1582 }
1583 
1584 // ****************
1585 // Utility routines
1586 // ****************
1587 
1588 inline G4double G4TankFastCerenkov::GetSphereIntersect(const G4ThreeVector& Pos,
1589  const G4double r)
1590  const
1591 {
1592  G4double b, c, d;
1593  G4double DistA, DistB;
1594 
1595  b = -Pos.dot(fPhotonMomentum);
1596  c = Pos.dot(Pos) - (r * r);
1597  d = (b * b) - c;
1598  if (d < 0.0) return 1.0e99; // no intersection with sphere at all
1599  d = sqrt(d);
1600  DistA = b + d;
1601  DistB = b - d;
1602  if (DistA > 0.0) {
1603  if (DistB < DistA) {
1604  if(DistB > 0.0) return DistB;
1605  return DistA; // inside sphere - B is behind us, A ahead
1606  }
1607  return DistA;
1608  }
1609  else {
1610  if (DistB > 0.0) return DistB; // inside sphere - A is behind us, B ahead
1611  }
1612  // if we get here, both are < 0 - intersections are both behind us already
1613  return 1.0e99;
1614 }
1615 
1616 inline G4double G4TankFastCerenkov::GetEllipsoidIntersect(const G4ThreeVector& Pos,
1617  const G4double r1, const G4double r2)
1618  const
1619 {
1620  G4double a1, a2, b[3], c[3], d[3], e, f, g;
1621  // G4double DistA, DistB;
1622 
1623  // The equation of the line with the photon direction is given by Pos+a*fPhotonMomentum being a the
1624  // distance from the photon position since fPhotonMomentum is a unitary vector in the direction of
1625  // the photon
1626 
1627  b[0] = fPhotonMomentum.x()*fPhotonMomentum.x();
1628  b[1] = fPhotonMomentum.y()*fPhotonMomentum.y();
1629  b[2] = fPhotonMomentum.z()*fPhotonMomentum.z();
1630  c[0] = Pos.x()*Pos.x();
1631  c[1] = Pos.y()*Pos.y();
1632  c[2] = Pos.z()*Pos.z();
1633  d[0] = fPhotonMomentum.x()*Pos.x();
1634  d[1] = fPhotonMomentum.y()*Pos.y();
1635  d[2] = fPhotonMomentum.z()*Pos.z();
1636 
1637  e = (b[0]+b[1])/(r1*r1) + b[2]/(r2*r2);
1638  f = 2.*((d[0]+d[1])/(r1*r1) + d[2]/(r2*r2));
1639  g = (c[0]+c[1])/(r1*r1)+c[2]/(r2*r2) - 1.;
1640 
1641  // if no solution of the quadratic equation there is no intersection
1642  if ( (f*f-4*e*g) < 0 ) {
1643  return 1.0e99;
1644  }
1645  else { // take smallest positive value of the solution
1646  a1 = 1./(2*e) * ( -f + sqrt(f*f-4*e*g) );
1647  a2 = 1./(2*e) * ( -f - sqrt(f*f-4*e*g) );
1648  if ( a2 > 10e-5 ) // to avoid almost 0 but positive a2 for current photon position
1649  return a2;
1650  else if ( a1 > 0 )
1651  return a1;
1652  }
1653 
1654  return 1.0e99;
1655 
1656 }
1657 
1658 
1659 //not used
1660 // G4double G4TankFastCerenkov::RandGaussZero(const G4double Sigma)
1661 // {
1662 // // generates Gaussian random numbers with given Sigma and mean of 0
1663 // static G4int i;
1664 // static G4double y1, y2;
1665 // G4double x1, x2, w;
1666 
1667 // i = 1 - i;
1668 // if (i == 0) return Sigma * y2;
1669 // do {
1670 // x1 = 2.0 * G4UniformRand() - 1.0;
1671 // x2 = 2.0 * G4UniformRand() - 1.0;
1672 // w = x1 * x1 + x2 * x2;
1673 // } while (w >= 1.0);
1674 // w = sqrt((-2.0 * log(w)) / w);
1675 // y1 = x1 * w;
1676 // y2 = x2 * w;
1677 // return Sigma * y1;
1678 // }
#define TARGET_PMT2
const double eV
Definition: GalacticUnits.h:35
unsigned int GetNPoints() const
Point object.
Definition: Point.h:32
#define INWARD
Detector description interface for Station-related data.
#define DOWN_IN_TANK
const double eplus
Definition: GalacticUnits.h:37
#define OUTWARD
ArrayIterator XEnd()
end of array of X
CoordinateSystemPtr GetCoordinateSystem() const
Get the coordinate system of the current internal representation.
Definition: BasicVector.h:234
Class to hold collection (x,y) points and provide interpolation between them.
#define TARGET_PMT1
const PMT & GetPMT(const int id) const
Get specified PMT by id.
double pow(const double x, const unsigned int i)
Exception for reporting variable out of valid range.
constexpr double MeV
Definition: AugerUnits.h:184
void AddPhoton(const int nPMT, const double peTime, const bool IsFromMuonDecay)
Definition: G4TankPMT.cc:48
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
double GetCollectionEfficiency() const
Collection efficiency.
#define IN_INTERFACE_3
#define IN_DOME_1
double GetX(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:206
constexpr double kPi
Definition: MathConstants.h:24
#define IN_DOME_2
G4double GetAverageNumberOfPhotons(const G4DynamicParticle *, const G4Material *, const G4MaterialPropertyVector *) const
constexpr double m3
Definition: AugerUnits.h:123
constexpr double g
Definition: AugerUnits.h:200
double Beta(const double energy)
Calculate the electron energy versus the relativistic beta.
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
#define IN_DOME_3
constexpr double kelvin
Definition: AugerUnits.h:259
#define IN_INTERFACE_1
ArrayIterator YBegin()
begin of array of Y
#define TARGET_FLOOR
double GetY(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:209
double Distance(const Point &p, const sdet::Station &s)
ArrayIterator XBegin()
begin of array of X
const utl::TabulatedFunction & GetQuantumEfficiency() const
Quantum efficiency.
constexpr double cm
Definition: AugerUnits.h:117
G4double GetEllipsoidIntersect(const G4ThreeVector &, const G4double, const G4double) const
ArrayIterator YEnd()
end of array of Y
double GetZ(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:212
double Y(const double x) const
Get or interpolate the Y value that corresponds to parameter x.
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
constexpr double m
Definition: AugerUnits.h:121
#define TARGET_WALL
#define IN_TANK
#define TARGET_PMT3
#define IN_INTERFACE_2

, generated on Tue Sep 26 2023.