CMS 3D CMS Logo

CastorSD.cc
Go to the documentation of this file.
1 // File: CastorSD.cc
3 // Date: 02.04
4 // UpDate: 07.04 - C3TF & C4TF semi-trapezoid added
5 // Description: Sensitive Detector class for Castor
7 
11 
14 
15 #include "G4SDManager.hh"
16 #include "G4Step.hh"
17 #include "G4Track.hh"
18 #include "G4VProcess.hh"
19 
20 #include "G4ios.hh"
21 #include "G4Cerenkov.hh"
22 #include "G4LogicalVolumeStore.hh"
23 
24 #include "CLHEP/Units/GlobalSystemOfUnits.h"
25 #include "CLHEP/Units/GlobalPhysicalConstants.h"
26 #include "Randomize.hh"
27 #include "G4Poisson.hh"
28 
29 //#define debugLog
30 
32  const edm::EventSetup& es,
33  const SensitiveDetectorCatalog& clg,
34  edm::ParameterSet const& p,
35  const SimTrackManager* manager)
36  : CaloSD(name, es, clg, p, manager),
38  lvC3EF(nullptr),
39  lvC3HF(nullptr),
40  lvC4EF(nullptr),
41  lvC4HF(nullptr),
42  lvCAST(nullptr) {
43  edm::ParameterSet m_CastorSD = p.getParameter<edm::ParameterSet>("CastorSD");
44  useShowerLibrary = m_CastorSD.getParameter<bool>("useShowerLibrary");
45  energyThresholdSL = m_CastorSD.getParameter<double>("minEnergyInGeVforUsingSLibrary");
46  energyThresholdSL = energyThresholdSL * GeV; // Convert GeV => MeV
47 
48  non_compensation_factor = m_CastorSD.getParameter<double>("nonCompensationFactor");
49 
50  if (useShowerLibrary) {
51  showerLibrary = new CastorShowerLibrary(name, p);
52  setParameterized(true);
53  }
55 
56  edm::LogInfo("ForwardSim") << "********************************************************\n"
57  << "* Constructing a CastorSD with name " << GetName() << "\n"
58  << "* Using Castor Shower Library: " << useShowerLibrary << "\n"
59  << "********************************************************";
60 
61  const G4LogicalVolumeStore* lvs = G4LogicalVolumeStore::GetInstance();
62  for (auto lv : *lvs) {
63  if (strcmp((lv->GetName()).c_str(), "C3EF") == 0) {
64  lvC3EF = lv;
65  }
66  if (strcmp((lv->GetName()).c_str(), "C3HF") == 0) {
67  lvC3HF = lv;
68  }
69  if (strcmp((lv->GetName()).c_str(), "C4EF") == 0) {
70  lvC4EF = lv;
71  }
72  if (strcmp((lv->GetName()).c_str(), "C4HF") == 0) {
73  lvC4HF = lv;
74  }
75  if (strcmp((lv->GetName()).c_str(), "CAST") == 0) {
76  lvCAST = lv;
77  }
78  if (lvC3EF != nullptr && lvC3HF != nullptr && lvC4EF != nullptr && lvC4HF != nullptr && lvCAST != nullptr) {
79  break;
80  }
81  }
82  edm::LogInfo("ForwardSim") << "CastorSD:: LogicalVolume pointers\n"
83  << lvC3EF << " for C3EF; " << lvC3HF << " for C3HF; " << lvC4EF << " for C4EF; " << lvC4HF
84  << " for C4HF; " << lvCAST << " for CAST. ";
85 }
86 
87 //=============================================================================================
88 
90 
91 //=============================================================================================
92 
93 double CastorSD::getEnergyDeposit(const G4Step* aStep) {
94  double NCherPhot = 0.;
95 
96  // Get theTrack
97  auto const theTrack = aStep->GetTrack();
98 
99  // preStepPoint information *********************************************
100  auto const preStepPoint = aStep->GetPreStepPoint();
101  auto const currentPV = preStepPoint->GetPhysicalVolume();
102  auto const currentLV = currentPV->GetLogicalVolume();
103 
104 #ifdef debugLog
105  edm::LogInfo("ForwardSim") << "CastorSD::getEnergyDeposit:"
106  << "\n CurrentStepNumber , TrackID , ParentID, Particle , VertexPosition ,"
107  << " LogicalVolumeAtVertex , PV, Time"
108  << "\n TRACKINFO: " << theTrack->GetCurrentStepNumber() << " , " << theTrack->GetTrackID()
109  << " , " << theTrack->GetParentID() << " , "
110  << theTrack->GetDefinition()->GetParticleName() << " , " << theTrack->GetVertexPosition()
111  << " , " << theTrack->GetLogicalVolumeAtVertex()->GetName() << " , "
112  << currentPV->GetName() << " , " << theTrack->GetGlobalTime();
113 #endif
114 
115  // if particle moves from interaction point or "backwards (halo)
116  const G4ThreeVector& hit_mom = preStepPoint->GetMomentumDirection();
117  const G4ThreeVector& hitPoint = preStepPoint->GetPosition();
118  double zint = hitPoint.z();
119 
120  // Check if theTrack is a muon (if so, DO NOT use Shower Library)
121  G4int parCode = theTrack->GetDefinition()->GetPDGEncoding();
122  bool isHad = G4TrackToParticleID::isStableHadronIon(theTrack);
123 
124  double meanNCherPhot = 0;
125  G4double charge = preStepPoint->GetCharge();
126  // VI: no Cerenkov light from neutrals
127  if (0.0 == charge) {
128  return meanNCherPhot;
129  }
130 
131  G4double beta = preStepPoint->GetBeta();
132  const double bThreshold = 0.67;
133  // VI: no Cerenkov light from non-relativistic particles
134  if (beta < bThreshold) {
135  return meanNCherPhot;
136  }
137 
138  // remember primary particle hitting the CASTOR detector
139  TrackInformationExtractor TIextractor;
140  TrackInformation& trkInfo = TIextractor(theTrack);
141  if (!trkInfo.hasCastorHit()) {
142  trkInfo.setCastorHitPID(parCode);
143  }
144  G4double stepl = aStep->GetStepLength() / cm;
145 
146  //int castorHitPID = trkInfo.hasCastorHit() ? std::abs(trkInfo.getCastorHitPID())
147  // : std::abs(parCode);
148 
149  // *************************************************
150  // take into account light collection curve for plate
151  // double weight = curve_Castor(nameVolume, preStepPoint);
152  // double edep = aStep->GetTotalEnergyDeposit() * weight;
153  // *************************************************
154 
155  // *************************************************
156  /* comments for sensitive volumes:
157  C001 ...-... CP06,CBU1 ...-...CALM --- > fibres and bundle
158  for first release of CASTOR
159  CASF --- > quartz plate for first and second releases of CASTOR
160  GF2Q, GFNQ, GR2Q, GRNQ
161  for tests with my own test geometry of HF (on ask of Gavrilov)
162  C3TF, C4TF - for third release of CASTOR
163  */
164 #ifdef debugLog
165  edm::LogInfo("ForwardSim") << "CastorSD::getEnergyDeposit: for ID=" << theTrack->GetTrackID()
166  << " LV: " << currentLV->GetName() << " isHad:" << isHad << " pdg=" << parCode
167  << " castorPID=" << trkInfo.getCastorHitPID() << " sl= " << stepl
168  << " Edep= " << aStep->GetTotalEnergyDeposit();
169 #endif
170  if (currentLV == lvC3EF || currentLV == lvC4EF || currentLV == lvC3HF || currentLV == lvC4HF) {
171  const double nMedium = 1.4925;
172  // double photEnSpectrDL = (1./400.nm-1./700.nm)*10000000.cm/nm; /* cm-1 */
173  // double photEnSpectrDL = 10714.285714;
174 
175  const double photEnSpectrDE = 1.24; /* see below */
176  /* E = 2pi*(1./137.)*(eV*cm/370.)/lambda = */
177  /* = 12.389184*(eV*cm)/lambda */
178  /* Emax = 12.389184*(eV*cm)/400nm*10-7cm/nm = 3.01 eV */
179  /* Emin = 12.389184*(eV*cm)/700nm*10-7cm/nm = 1.77 eV */
180  /* delE = Emax - Emin = 1.24 eV --> */
181  /* */
182  /* default for Castor nameVolume == "CASF" or (C3TF & C4TF) */
183 
184  const double thFullRefl = 23.; /* 23.dergee */
185  const double thFullReflRad = thFullRefl * pi / 180.;
186 
187  /* default for Castor nameVolume == "CASF" or (C3TF & C4TF) */
188  const double thFibDir = 45.; /* .dergee */
189  /* for test HF geometry volumes:
190  if(nameVolume == "GF2Q" || nameVolume == "GFNQ" ||
191  nameVolume == "GR2Q" || nameVolume == "GRNQ")
192  thFibDir = 0.0; // .dergee
193  */
194  const double thFibDirRad = thFibDir * pi / 180.;
195 
196  // theta of charged particle in LabRF(hit momentum direction):
197  double costh =
198  hit_mom.z() / sqrt(hit_mom.x() * hit_mom.x() + hit_mom.y() * hit_mom.y() + hit_mom.z() * hit_mom.z());
199  if (zint < 0)
200  costh = -costh;
201  double th = acos(std::min(std::max(costh, double(-1.)), double(1.)));
202 
203  // just in case (can do bot use):
204  if (th < 0.)
205  th += twopi;
206 
207  // theta of cone with Cherenkov photons w.r.t.direction of charged part.:
208  double costhcher = 1. / (nMedium * beta);
209  double thcher = acos(std::min(std::max(costhcher, double(-1.)), double(1.)));
210 
211  // diff thetas of charged part. and quartz direction in LabRF:
212  double DelFibPart = std::abs(th - thFibDirRad);
213 
214  // define real distances:
215  double d = std::abs(tan(th) - tan(thFibDirRad));
216 
217  double a = tan(thFibDirRad) + tan(std::abs(thFibDirRad - thFullReflRad));
218  double r = tan(th) + tan(std::abs(th - thcher));
219 
220  // define losses d_qz in cone of full reflection inside quartz direction
221  double d_qz;
222 #ifdef debugLog
223  int variant(0);
224 #endif
225  if (DelFibPart > (thFullReflRad + thcher)) {
226  d_qz = 0.;
227  } else {
228  if ((th + thcher) < (thFibDirRad + thFullReflRad) && (th - thcher) > (thFibDirRad - thFullReflRad)) {
229  d_qz = 1.;
230 #ifdef debugLog
231  variant = 1;
232 #endif
233  } else {
234  if ((thFibDirRad + thFullReflRad) < (th + thcher) && (thFibDirRad - thFullReflRad) > (th - thcher)) {
235  d_qz = 0.;
236 #ifdef debugLog
237  variant = 2;
238 #endif
239  } else {
240 #ifdef debugLog
241  variant = 3;
242 #endif
243  // use crossed length of circles(cone projection)
244  // dC1/dC2 :
245  double arg_arcos = 0.;
246  double tan_arcos = 2. * a * d;
247  if (tan_arcos != 0.)
248  arg_arcos = (r * r - a * a - d * d) / tan_arcos;
249  arg_arcos = std::abs(arg_arcos);
250  double th_arcos = acos(std::min(std::max(arg_arcos, -1.), 1.));
251  d_qz = std::abs(th_arcos / CLHEP::twopi);
252  }
253  }
254  }
255 
256  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
257 
258  meanNCherPhot = 370. * charge * charge * (1. - 1. / (nMedium * nMedium * beta * beta)) * photEnSpectrDE * stepl;
259 
260  double scale = isHad ? non_compensation_factor : 1.0;
261 
262  int poissNCherPhot = std::max(0, (int)G4Poisson(meanNCherPhot * scale));
263 
264  const double effPMTandTransport = 0.19;
265  const double ReflPower = 0.1;
266  double proba = d_qz + (1 - d_qz) * ReflPower;
267  NCherPhot = poissNCherPhot * effPMTandTransport * proba * 0.307;
268 #ifdef debugLog
269  edm::LogInfo("ForwardSim") << " Nph= " << NCherPhot << " Np= " << poissNCherPhot << " eff= " << effPMTandTransport
270  << " pb= " << proba << " Nmean= " << meanNCherPhot << " q=" << charge << " beta=" << beta
271  << " nMedium= " << nMedium << " sl= " << stepl << " Nde=" << photEnSpectrDE;
272 #endif
273  }
274  return NCherPhot;
275 }
276 
277 //=======================================================================================
278 
279 uint32_t CastorSD::setDetUnitId(const G4Step* aStep) {
280  return (numberingScheme == nullptr ? 0 : numberingScheme->getUnitID(aStep));
281 }
282 
283 //=======================================================================================
284 
286  if (scheme != nullptr) {
287  edm::LogInfo("ForwardSim") << "CastorSD: updates numbering scheme for " << GetName();
288  delete numberingScheme;
290  }
291 }
292 
293 //=======================================================================================
294 
295 uint32_t CastorSD::rotateUnitID(uint32_t unitID, const G4Track* track, const CastorShowerEvent& shower) {
296  // ==============================================================
297  //
298  // o Exploit Castor phi symmetry to return newUnitID for
299  // shower hits based on track phi coordinate
300  //
301  // ==============================================================
302 
303  // Get 'track' phi:
304  double trackPhi = track->GetPosition().phi();
305  if (trackPhi < 0)
306  trackPhi += 2 * M_PI;
307  // Get phi from primary that gave rise to SL 'shower':
308  double showerPhi = shower.getPrimPhi();
309  if (showerPhi < 0)
310  showerPhi += 2 * M_PI;
311  // Delta phi:
312 
313  // Find the OctSector for which 'track' and 'shower' belong
314  int trackOctSector = (int)(trackPhi / (M_PI / 4));
315  int showerOctSector = (int)(showerPhi / (M_PI / 4));
316 
317  uint32_t newUnitID;
318  uint32_t sec = ((unitID >> 4) & 0xF);
319  uint32_t complement = (unitID & 0xFFFFFF0F);
320 
321  // Get 'track' z:
322  double trackZ = track->GetPosition().z();
323 
324  int aux;
325  int dSec = 2 * (trackOctSector - showerOctSector);
326  if (trackZ > 0) // Good for revision 1.9 of CastorNumberingScheme
327  {
328  int sec1 = sec - dSec;
329  // sec -= dSec ;
330  if (sec1 < 0)
331  sec1 += 16;
332  if (sec1 > 15)
333  sec1 -= 16;
334  sec = (uint32_t)(sec1);
335  } else {
336  if (dSec < 0)
337  sec += 16;
338  sec += dSec;
339  aux = (int)(sec / 16);
340  sec -= aux * 16;
341  }
342  sec = sec << 4;
343  newUnitID = complement | sec;
344 
345 #ifdef debugLog
346  if (newUnitID != unitID) {
347  LogDebug("ForwardSim") << "\n CastorSD::rotateUnitID: "
348  << "\n unitID = " << unitID << "\n newUnitID = " << newUnitID;
349  }
350 #endif
351 
352  return newUnitID;
353 }
354 
355 //=======================================================================================
356 
357 bool CastorSD::getFromLibrary(const G4Step* aStep) {
359  //
360  // Method to get hits from the Shower Library
361  //
362  // "updateHit" save the Hits to a CaloG4Hit container
363  //
365 
366  auto const theTrack = aStep->GetTrack();
367  auto parCode = theTrack->GetDefinition()->GetPDGEncoding();
368 
369  // remember primary particle hitting the CASTOR detector
370  TrackInformationExtractor TIextractor;
371  TrackInformation& trkInfo = TIextractor(theTrack);
372  if (!trkInfo.hasCastorHit()) {
373  trkInfo.setCastorHitPID(parCode);
374  }
375 
376  if (!useShowerLibrary) {
377  return false;
378  }
379 
380  // preStepPoint information *********************************************
381 
382  auto const preStepPoint = aStep->GetPreStepPoint();
383  auto const currentPV = preStepPoint->GetPhysicalVolume();
384  auto const currentLV = currentPV->GetLogicalVolume();
385 
386 #ifdef debugLog
387  edm::LogInfo("ForwardSim") << "CastorSD::getFromLibrary: for ID=" << theTrack->GetTrackID()
388  << " parentID= " << theTrack->GetParentID() << " "
389  << theTrack->GetDefinition()->GetParticleName() << " LV: " << currentLV->GetName()
390  << " PV: " << currentPV->GetName() << "\n eta= " << theTrack->GetPosition().eta()
391  << " phi= " << theTrack->GetPosition().phi()
392  << " z(cm)= " << theTrack->GetPosition().z() / cm
393  << " time(ns)= " << theTrack->GetGlobalTime()
394  << " E(GeV)= " << theTrack->GetTotalEnergy() / GeV;
395 
396 #endif
397 
398  const G4ThreeVector& hitPoint = preStepPoint->GetPosition();
399  const G4ThreeVector& hit_mom = preStepPoint->GetMomentumDirection();
400  double zint = hitPoint.z();
401  double pz = hit_mom.z();
402 
403  // Check if theTrack moves backward
404  bool backward = (pz * zint < 0.) ? true : false;
405 
406  // Check that theTrack is above the energy threshold to use Shower Library
407  bool aboveThreshold = (theTrack->GetKineticEnergy() > energyThresholdSL) ? true : false;
408 
409  // Check if theTrack is a muon (if so, DO NOT use Shower Library)
411  bool isHad = G4TrackToParticleID::isStableHadronIon(theTrack);
412 
413  // angle condition
414  double theta_max = M_PI - 3.1305; // angle in radians corresponding to -5.2 eta
415  double R_mom = sqrt(hit_mom.x() * hit_mom.x() + hit_mom.y() * hit_mom.y());
416  double theta = atan2(R_mom, std::abs(pz));
417  bool angleok = (theta < theta_max) ? true : false;
418 
419  // OkToUse
420  double R = sqrt(hitPoint.x() * hitPoint.x() + hitPoint.y() * hitPoint.y());
421  bool dot = (zint < -14450. && R < 45.) ? true : false;
422  bool inRange = (zint < -14700. || R > 193.) ? false : true;
423  //bool OkToUse = (inRange && !dot) ? true : false;
424 
425  bool particleWithinShowerLibrary =
426  aboveThreshold && (isEM || isHad) && (!backward) && inRange && !dot && angleok && currentLV == lvCAST;
427 
428 #ifdef debugLog
429  edm::LogInfo("ForwardSim") << "CastorSD::getFromLibrary: ID= " << theTrack->GetTrackID() << " E>E0 " << aboveThreshold
430  << " isEM " << isEM << " isHad " << isHad << " backword " << backward << " Ok "
431  << (inRange && !dot) << " angle " << angleok << " LV: " << currentLV->GetName() << " "
432  << (currentLV == lvCAST) << " " << particleWithinShowerLibrary
433  << " Edep= " << aStep->GetTotalEnergyDeposit();
434 #endif
435 
436  // Use Castor shower library if energy is above threshold, is not a muon
437  // and is not moving backward
438  if (!particleWithinShowerLibrary) {
439  return false;
440  }
441 
442  // **** Call method to retrieve hits from the ShowerLibrary ****
443  // always kill primary
444  bool isKilled(true);
446 
447  int primaryID = setTrackID(aStep);
448 
449  // Reset entry point for new primary
450  resetForNewPrimary(aStep);
451 
452 #ifdef debugLog
453  edm::LogInfo("ForwardSim") << "CastorSD::getFromLibrary: " << hits.getNhit() << " hits for " << GetName() << " from "
454  << theTrack->GetDefinition()->GetParticleName() << " of "
455  << preStepPoint->GetKineticEnergy() / GeV << " GeV and trackID " << theTrack->GetTrackID()
456  << " isHad: " << isHad;
457 #endif
458 
459  // Scale to correct energy
460  double E_track = preStepPoint->GetTotalEnergy();
461  double E_SLhit = hits.getPrimE() * CLHEP::GeV;
462  double scale = E_track / E_SLhit;
463 
464  //Non compensation
465  if (isHad) {
466  scale *= non_compensation_factor; // if hadronic extend the scale with the non-compensation factor
467  }
468  // Loop over hits retrieved from the library
469  for (unsigned int i = 0; i < hits.getNhit(); ++i) {
470  // Get nPhotoElectrons and set edepositEM / edepositHAD accordingly
471  double nPhotoElectrons = hits.getNphotons(i) * scale;
472 
473  if (isEM) {
474  edepositEM = nPhotoElectrons;
475  edepositHAD = 0.;
476  } else {
477  edepositEM = 0.;
478  edepositHAD = nPhotoElectrons;
479  }
480 
481  // Get hit position and time
482  double time = hits.getTime(i);
483 
484  // Get hit detID
485  unsigned int unitID = hits.getDetID(i);
486 
487  // Make the detID "rotation" from one sector to another taking into account the
488  // sectors of the impinging particle (theTrack) and of the particle that produced
489  // the 'hits' retrieved from shower library
490  unsigned int rotatedUnitID = rotateUnitID(unitID, theTrack, hits);
491  currentID.setID(rotatedUnitID, time, primaryID, 0);
492  processHit(aStep);
493  } // End of loop over hits
494  return isKilled;
495 }
#define LogDebug(id)
float edepositEM
Definition: CaloSD.h:129
T getParameter(std::string const &) const
double getEnergyDeposit(const G4Step *) override
Definition: CastorSD.cc:93
int getCastorHitPID() const
const double GeV
Definition: MathUtil.h:16
Definition: CaloSD.h:38
uint32_t setDetUnitId(const G4Step *step) override
Definition: CastorSD.cc:279
#define nullptr
Geom::Theta< T > theta() const
double non_compensation_factor
Definition: CastorSD.h:54
CastorNumberingScheme * numberingScheme
Definition: CastorSD.h:47
bool useShowerLibrary
Definition: CastorSD.h:52
void processHit(const G4Step *step)
Definition: CaloSD.h:103
unsigned int getDetID(int i)
G4LogicalVolume * lvC4HF
Definition: CastorSD.h:49
bool getFromLibrary(const G4Step *) override
Definition: CastorSD.cc:357
const Double_t pi
void setCastorHitPID(const int pid)
CastorShowerEvent getShowerHits(const G4Step *, bool &)
float edepositHAD
Definition: CaloSD.h:129
T sqrt(T t)
Definition: SSEVec.h:19
void resetForNewPrimary(const G4Step *)
Definition: CaloSD.cc:421
float getTime(int i)
Tan< T >::type tan(const T &t)
Definition: Tan.h:22
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
float getNphotons(int i)
G4LogicalVolume * lvC3HF
Definition: CastorSD.h:49
T min(T a, T b)
Definition: MathUtil.h:58
bool hasCastorHit() const
d
Definition: ztail.py:151
double energyThresholdSL
Definition: CastorSD.h:53
#define M_PI
static bool isStableHadronIon(const G4Track *)
void setID(uint32_t unitID, double timeSlice, int trackID, uint16_t depth=0)
Definition: CaloHitID.cc:40
G4LogicalVolume * lvC4EF
Definition: CastorSD.h:49
uint32_t rotateUnitID(uint32_t, const G4Track *, const CastorShowerEvent &)
Definition: CastorSD.cc:295
CaloHitID currentID
Definition: CaloSD.h:131
float getPrimPhi() const
~CastorSD() override
Definition: CastorSD.cc:89
G4LogicalVolume * lvCAST
Definition: CastorSD.h:50
CastorShowerLibrary * showerLibrary
Definition: CastorSD.h:48
void setNumberingScheme(CastorNumberingScheme *scheme)
Definition: CastorSD.cc:285
G4LogicalVolume * lvC3EF
Definition: CastorSD.h:49
virtual int setTrackID(const G4Step *)
Definition: CaloSD.cc:589
T dot(const Basic3DVector &v) const
Scalar product, or "dot" product, with a vector of same type.
double a
Definition: hdecay.h:119
virtual uint32_t getUnitID(const G4Step *aStep) const
static bool isGammaElectronPositron(int pdgCode)
static TrackerG4SimHitNumberingScheme & numberingScheme(const GeometricDet &det)
unsigned int getNhit()
void setParameterized(bool val)
Definition: CaloSD.h:100
CastorSD(const std::string &, const edm::EventSetup &, const SensitiveDetectorCatalog &clg, edm::ParameterSet const &, const SimTrackManager *)
Definition: CastorSD.cc:31