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