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

, generated on Tue Sep 26 2023.