CMS 3D CMS Logo

DreamSD.cc
Go to the documentation of this file.
1 
8 
9 #include "G4LogicalVolumeStore.hh"
10 #include "G4LogicalVolume.hh"
11 #include "G4Step.hh"
12 #include "G4Track.hh"
13 #include "G4VProcess.hh"
14 #include "G4Poisson.hh"
15 
16 // Histogramming
18 
19 // Cherenkov
22 
23 #include "G4SystemOfUnits.hh"
24 #include "G4PhysicalConstants.hh"
25 
26 //________________________________________________________________________________________
28  const SensitiveDetectorCatalog & clg,
29  edm::ParameterSet const & p, const SimTrackManager* manager) :
30  CaloSD(name, cpv, clg, p, manager) {
31 
33  useBirk= m_EC.getParameter<bool>("UseBirkLaw");
34  doCherenkov_ = m_EC.getParameter<bool>("doCherenkov");
35  birk1 = m_EC.getParameter<double>("BirkC1")*(g/(MeV*cm2));
36  birk2 = m_EC.getParameter<double>("BirkC2");
37  birk3 = m_EC.getParameter<double>("BirkC3");
38  slopeLY= m_EC.getParameter<double>("SlopeLightYield");
39  readBothSide_ = m_EC.getUntrackedParameter<bool>("ReadBothSide", false);
40 
41  chAngleIntegrals_.reset(nullptr);
42 
43  edm::LogInfo("EcalSim") << "Constructing a DreamSD with name " << GetName() << "\n"
44  << "DreamSD:: Use of Birks law is set to "
45  << useBirk << " with three constants kB = "
46  << birk1 << ", C1 = " << birk2 << ", C2 = "
47  << birk3 << "\n"
48  << " Slope for Light yield is set to "
49  << slopeLY << "\n"
50  << " Parameterization of Cherenkov is set to "
51  << doCherenkov_ << " and readout both sides is "
52  << readBothSide_;
53 
54  initMap(name,cpv);
55 
56 }
57 
58 //________________________________________________________________________________________
59 double DreamSD::getEnergyDeposit(const G4Step* aStep) {
60 
61  // take into account light collection curve for crystals
62  double weight = curve_LY(aStep, side);
63  if (useBirk) weight *= getAttenuation(aStep, birk1, birk2, birk3);
64  double edep = aStep->GetTotalEnergyDeposit() * weight;
65 
66  // Get Cerenkov contribution
67  if ( doCherenkov_ ) { edep += cherenkovDeposit_( aStep ); }
68  LogDebug("EcalSim") << "DreamSD:: " << aStep->GetPreStepPoint()->GetPhysicalVolume()->GetName()
69  << " Side " << side
70  <<" Light Collection Efficiency " << weight
71  << " Weighted Energy Deposit " << edep/MeV
72  << " MeV";
73 
74  return edep;
75 }
76 
77 //________________________________________________________________________________________
79 
80  // Get the material and set properties if needed
81  DimensionMap::const_iterator ite = xtalLMap.begin();
82  const G4LogicalVolume* lv = (ite->first);
83  G4Material* material = lv->GetMaterial();
84  edm::LogInfo("EcalSim") << "DreamSD::initRun: Initializes for material "
85  << material->GetName() << " in " << lv->GetName();
86  materialPropertiesTable = material->GetMaterialPropertiesTable();
87  if ( !materialPropertiesTable ) {
88  if ( !setPbWO2MaterialProperties_( material ) ) {
89  edm::LogWarning("EcalSim") << "Couldn't retrieve material properties table\n"
90  << " Material = " << material->GetName();
91  }
92  materialPropertiesTable = material->GetMaterialPropertiesTable();
93  }
94 }
95 
96 
97 //________________________________________________________________________________________
98 uint32_t DreamSD::setDetUnitId(const G4Step * aStep) {
99  const G4VTouchable* touch = aStep->GetPreStepPoint()->GetTouchable();
100  uint32_t id = (touch->GetReplicaNumber(1))*10 + (touch->GetReplicaNumber(0));
101  side = readBothSide_ ? -1 : 1;
102  if(side < 0) { ++id; }
103  LogDebug("EcalSim") << "DreamSD:: ID " << id;
104  return id;
105 }
106 
107 
108 //________________________________________________________________________________________
109 void DreamSD::initMap(const std::string& sd, const DDCompactView & cpv) {
110 
111  G4String attribute = "ReadOutName";
113  DDFilteredView fv(cpv,filter);
114  fv.firstChild();
115 
116  const G4LogicalVolumeStore * lvs = G4LogicalVolumeStore::GetInstance();
117  std::vector<G4LogicalVolume *>::const_iterator lvcite;
118  bool dodet=true;
119  while (dodet) {
120  const DDSolid & sol = fv.logicalPart().solid();
121  std::vector<double> paras(sol.parameters());
122  G4String name = sol.name().name();
123  G4LogicalVolume* lv=nullptr;
124  for (lvcite = lvs->begin(); lvcite != lvs->end(); lvcite++)
125  if ((*lvcite)->GetName() == name) {
126  lv = (*lvcite);
127  break;
128  }
129  LogDebug("EcalSim") << "DreamSD::initMap (for " << sd << "): Solid "
130  << name << " Shape " << sol.shape() <<" Parameter 0 = "
131  << paras[0] << " Logical Volume " << lv;
132  double length = 0, width = 0;
133  // Set length to be the largest size, width the smallest
134  std::sort( paras.begin(), paras.end() );
135  length = 2.0*paras.back();
136  width = 2.0*paras.front();
137  xtalLMap.insert( std::pair<G4LogicalVolume*,Doubles>(lv,Doubles(length,width)) );
138  dodet = fv.next();
139  }
140  LogDebug("EcalSim") << "DreamSD: Length Table for " << attribute << " = "
141  << sd << ":";
142  DimensionMap::const_iterator ite = xtalLMap.begin();
143  int i=0;
144  for (; ite != xtalLMap.end(); ite++, i++) {
145  G4String name = "Unknown";
146  if (ite->first != nullptr) name = (ite->first)->GetName();
147  LogDebug("EcalSim") << " " << i << " " << ite->first << " " << name
148  << " L = " << ite->second.first
149  << " W = " << ite->second.second;
150  }
151 }
152 
153 //________________________________________________________________________________________
154 double DreamSD::curve_LY(const G4Step* aStep, int flag) {
155 
156  auto const stepPoint = aStep->GetPreStepPoint();
157  auto const lv = stepPoint->GetTouchable()->GetVolume(0)->GetLogicalVolume();
158  G4String nameVolume= lv->GetName();
159 
160  double weight = 1.;
161  G4ThreeVector localPoint = setToLocal(stepPoint->GetPosition(),
162  stepPoint->GetTouchable());
163  double crlength = crystalLength(lv);
164  double localz = localPoint.x();
165  double dapd = 0.5 * crlength - flag*localz; // Distance from closest APD
166  if (dapd >= -0.1 || dapd <= crlength+0.1) {
167  if (dapd <= 100.)
168  weight = 1.0 + slopeLY - dapd * 0.01 * slopeLY;
169  } else {
170  edm::LogWarning("EcalSim") << "DreamSD: light coll curve : wrong distance "
171  << "to APD " << dapd << " crlength = "
172  << crlength << " crystal name = " << nameVolume
173  << " z of localPoint = " << localz
174  << " take weight = " << weight;
175  }
176  LogDebug("EcalSim") << "DreamSD, light coll curve : " << dapd
177  << " crlength = " << crlength
178  << " crystal name = " << nameVolume
179  << " z of localPoint = " << localz
180  << " take weight = " << weight;
181  return weight;
182 }
183 
184 //________________________________________________________________________________________
185 double DreamSD::crystalLength(G4LogicalVolume* lv) const {
186 
187  double length= -1.;
188  DimensionMap::const_iterator ite = xtalLMap.find(lv);
189  if (ite != xtalLMap.end()) length = ite->second.first;
190  return length;
191 
192 }
193 
194 //________________________________________________________________________________________
195 double DreamSD::crystalWidth(G4LogicalVolume* lv) const {
196 
197  double width= -1.;
198  DimensionMap::const_iterator ite = xtalLMap.find(lv);
199  if (ite != xtalLMap.end()) width = ite->second.second;
200  return width;
201 
202 }
203 
204 
205 //________________________________________________________________________________________
206 // Calculate total cherenkov deposit
207 // Inspired by Geant4's Cherenkov implementation
208 double DreamSD::cherenkovDeposit_(const G4Step* aStep ) {
209 
210  double cherenkovEnergy = 0;
211  if (!materialPropertiesTable) return cherenkovEnergy;
212  G4Material* material = aStep->GetTrack()->GetMaterial();
213 
214  // Retrieve refractive index
215  G4MaterialPropertyVector* Rindex = materialPropertiesTable->GetProperty("RINDEX");
216  if ( Rindex == nullptr ) {
217  edm::LogWarning("EcalSim") << "Couldn't retrieve refractive index";
218  return cherenkovEnergy;
219  }
220 
221  // V.Ivanchenko - temporary close log output for 9.5
222  // Material refraction properties
223  int Rlength = Rindex->GetVectorLength() - 1;
224  double Pmin = Rindex->Energy(0);
225  double Pmax = Rindex->Energy(Rlength);
226  LogDebug("EcalSim") << "Material properties: " << "\n"
227  << " Pmin = " << Pmin
228  << " Pmax = " << Pmax;
229 
230  // Get particle properties
231  const G4StepPoint* pPreStepPoint = aStep->GetPreStepPoint();
232  const G4StepPoint* pPostStepPoint = aStep->GetPostStepPoint();
233  const G4ThreeVector& x0 = pPreStepPoint->GetPosition();
234  G4ThreeVector p0 = aStep->GetDeltaPosition().unit();
235  const G4DynamicParticle* aParticle = aStep->GetTrack()->GetDynamicParticle();
236  const double charge = aParticle->GetDefinition()->GetPDGCharge();
237  // beta is averaged over step
238  double beta = 0.5*( pPreStepPoint->GetBeta() + pPostStepPoint->GetBeta() );
239  double BetaInverse = 1.0/beta;
240 
241  LogDebug("EcalSim") << "Particle properties: " << "\n"
242  << " charge = " << charge
243  << " beta = " << beta;
244 
245  // Now get number of photons generated in this step
246  double meanNumberOfPhotons = getAverageNumberOfPhotons_( charge, beta, material, Rindex );
247  if ( meanNumberOfPhotons <= 0.0 ) { // Don't do anything
248  LogDebug("EcalSim") << "Mean number of photons is zero: " << meanNumberOfPhotons
249  << ", stopping here";
250  return cherenkovEnergy;
251  }
252 
253  // number of photons is in unit of Geant4...
254  meanNumberOfPhotons *= aStep->GetStepLength();
255 
256  // Now get a poisson distribution
257  int numPhotons = static_cast<int>( G4Poisson(meanNumberOfPhotons) );
258  //edm::LogVerbatim("EcalSim") << "Number of photons = " << numPhotons;
259  if ( numPhotons <= 0 ) {
260  LogDebug("EcalSim") << "Poission number of photons is zero: " << numPhotons
261  << ", stopping here";
262  return cherenkovEnergy;
263  }
264 
265  // Material refraction properties
266  double dp = Pmax - Pmin;
267  double maxCos = BetaInverse / (*Rindex)[Rlength];
268  double maxSin2 = (1.0 - maxCos) * (1.0 + maxCos);
269 
270  // Finally: get contribution of each photon
271  for ( int iPhoton = 0; iPhoton<numPhotons; ++iPhoton ) {
272 
273  // Determine photon momentum
274  double randomNumber;
275  double sampledMomentum, sampledRI;
276  double cosTheta, sin2Theta;
277 
278  // sample a momentum (not sure why this is needed!)
279  do {
280  randomNumber = G4UniformRand();
281  sampledMomentum = Pmin + randomNumber * dp;
282  sampledRI = Rindex->Value(sampledMomentum);
283  cosTheta = BetaInverse / sampledRI;
284 
285  sin2Theta = (1.0 - cosTheta)*(1.0 + cosTheta);
286  randomNumber = G4UniformRand();
287 
288  } while (randomNumber*maxSin2 > sin2Theta);
289 
290  // Generate random position of photon on cone surface
291  // defined by Theta
292  randomNumber = G4UniformRand();
293 
294  double phi = twopi*randomNumber;
295  double sinPhi = sin(phi);
296  double cosPhi = cos(phi);
297 
298  // Create photon momentum direction vector
299  // The momentum direction is still w.r.t. the coordinate system where the primary
300  // particle direction is aligned with the z axis
301  double sinTheta = sqrt(sin2Theta);
302  double px = sinTheta*cosPhi;
303  double py = sinTheta*sinPhi;
304  double pz = cosTheta;
305  G4ThreeVector photonDirection(px, py, pz);
306 
307  // Rotate momentum direction back to global (crystal) reference system
308  photonDirection.rotateUz(p0);
309 
310  // Create photon position and momentum
311  randomNumber = G4UniformRand();
312  G4ThreeVector photonPosition = x0 + randomNumber * aStep->GetDeltaPosition();
313  G4ThreeVector photonMomentum = sampledMomentum*photonDirection;
314 
315  // Collect energy on APD
316  cherenkovEnergy += getPhotonEnergyDeposit_( photonMomentum, photonPosition, aStep );
317  }
318  return cherenkovEnergy;
319 }
320 
321 
322 //________________________________________________________________________________________
323 // Returns number of photons produced per GEANT-unit (millimeter) in the current medium.
324 // From G4Cerenkov.cc
326  const double beta,
327  const G4Material* aMaterial,
328  const G4MaterialPropertyVector* Rindex )
329 {
330  const double rFact = 369.81/(eV * cm);
331 
332  if( beta <= 0.0 ) return 0.0;
333 
334  double BetaInverse = 1./beta;
335 
336  // Vectors used in computation of Cerenkov Angle Integral:
337  // - Refraction Indices for the current material
338  // - new G4PhysicsOrderedFreeVector allocated to hold CAI's
339 
340  // Min and Max photon momenta
341  int Rlength = Rindex->GetVectorLength() - 1;
342  double Pmin = Rindex->Energy(0);
343  double Pmax = Rindex->Energy(Rlength);
344 
345  // Min and Max Refraction Indices
346  double nMin = (*Rindex)[0];
347  double nMax = (*Rindex)[Rlength];
348 
349  // Max Cerenkov Angle Integral
350  double CAImax = chAngleIntegrals_.get()->GetMaxValue();
351 
352  double dp = 0., ge = 0., CAImin = 0.;
353 
354  // If n(Pmax) < 1/Beta -- no photons generated
355  if ( nMax < BetaInverse) { }
356 
357  // otherwise if n(Pmin) >= 1/Beta -- photons generated
358  else if (nMin > BetaInverse) {
359  dp = Pmax - Pmin;
360  ge = CAImax;
361  }
362  // If n(Pmin) < 1/Beta, and n(Pmax) >= 1/Beta, then
363  // we need to find a P such that the value of n(P) == 1/Beta.
364  // Interpolation is performed by the GetPhotonEnergy() and
365  // GetProperty() methods of the G4MaterialPropertiesTable and
366  // the GetValue() method of G4PhysicsVector.
367  else {
368  Pmin = Rindex->Value(BetaInverse);
369  dp = Pmax - Pmin;
370  // need boolean for current implementation of G4PhysicsVector
371  // ==> being phased out
372  double CAImin = chAngleIntegrals_->Value(Pmin);
373  ge = CAImax - CAImin;
374 
375  }
376 
377  // Calculate number of photons
378  double numPhotons = rFact * charge/eplus * charge/eplus *
379  (dp - ge * BetaInverse*BetaInverse);
380 
381  LogDebug("EcalSim") << "@SUB=getAverageNumberOfPhotons"
382  << "CAImin = " << CAImin << "\n"
383  << "CAImax = " << CAImax << "\n"
384  << "dp = " << dp << ", ge = " << ge << "\n"
385  << "numPhotons = " << numPhotons;
386 
387 
388 
389  return numPhotons;
390 
391 }
392 
393 
394 //________________________________________________________________________________________
395 // Set lead tungstate material properties on the fly.
396 // Values from Ts42 detector construction
397 bool DreamSD::setPbWO2MaterialProperties_( G4Material* aMaterial ) {
398 
399  std::string pbWO2Name("E_PbWO4");
400  if ( pbWO2Name != aMaterial->GetName() ) { // Wrong material!
401  edm::LogWarning("EcalSim") << "This is not the right material: "
402  << "expecting " << pbWO2Name
403  << ", got " << aMaterial->GetName();
404  return false;
405  }
406 
407  G4MaterialPropertiesTable* table = new G4MaterialPropertiesTable();
408 
409  // Refractive index as a function of photon momentum
410  // FIXME: Should somehow put that in the configuration
411  const int nEntries = 14;
412  double PhotonEnergy[nEntries] = { 1.7712*eV, 1.8368*eV, 1.90745*eV, 1.98375*eV, 2.0664*eV,
413  2.15625*eV, 2.25426*eV, 2.3616*eV, 2.47968*eV, 2.61019*eV,
414  2.75521*eV, 2.91728*eV, 3.09961*eV, 3.30625*eV };
415  double RefractiveIndex[nEntries] = { 2.17728, 2.18025, 2.18357, 2.18753, 2.19285,
416  2.19813, 2.20441, 2.21337, 2.22328, 2.23619,
417  2.25203, 2.27381, 2.30282, 2.34666 };
418 
419  table->AddProperty( "RINDEX", PhotonEnergy, RefractiveIndex, nEntries );
420  aMaterial->SetMaterialPropertiesTable(table); // FIXME: could this leak? What does G4 do?
421 
422  // Calculate Cherenkov angle integrals:
423  // This is an ad-hoc solution (we hold it in the class, not in the material)
424  chAngleIntegrals_.reset(new G4PhysicsOrderedFreeVector());
425 
426  int index = 0;
427  double currentRI = RefractiveIndex[index];
428  double currentPM = PhotonEnergy[index];
429  double currentCAI = 0.0;
430  chAngleIntegrals_.get()->InsertValues(currentPM, currentCAI);
431  double prevPM = currentPM;
432  double prevCAI = currentCAI;
433  double prevRI = currentRI;
434  while ( ++index < nEntries ) {
435  currentRI = RefractiveIndex[index];
436  currentPM = PhotonEnergy[index];
437  currentCAI = 0.5*(1.0/(prevRI*prevRI) + 1.0/(currentRI*currentRI));
438  currentCAI = prevCAI + (currentPM - prevPM) * currentCAI;
439 
440  chAngleIntegrals_.get()->InsertValues(currentPM, currentCAI);
441 
442  prevPM = currentPM;
443  prevCAI = currentCAI;
444  prevRI = currentRI;
445  }
446 
447  LogDebug("EcalSim") << "Material properties set for " << aMaterial->GetName();
448 
449  return true;
450 
451 }
452 
453 
454 //________________________________________________________________________________________
455 // Calculate energy deposit of a photon on APD
456 // - simple tracing to APD position (straight line);
457 // - configurable reflection probability if not straight to APD;
458 // - APD response function
459 double DreamSD::getPhotonEnergyDeposit_( const G4ThreeVector& p,
460  const G4ThreeVector& x,
461  const G4Step* aStep )
462 {
463 
464  double energy = 0;
465 
466  // Crystal dimensions
467 
468  //edm::LogVerbatim("EcalSim") << p << x;
469 
470  // 1. Check if this photon goes straight to the APD:
471  // - assume that APD is at x=xtalLength/2.0
472  // - extrapolate from x=x0 to x=xtalLength/2.0 using momentum in x-y
473 
474  G4StepPoint* stepPoint = aStep->GetPreStepPoint();
475  G4LogicalVolume* lv = stepPoint->GetTouchable()->GetVolume(0)->GetLogicalVolume();
476  G4String nameVolume= lv->GetName();
477 
478  double crlength = crystalLength(lv);
479  double crwidth = crystalWidth(lv);
480  double dapd = 0.5 * crlength - x.x(); // Distance from closest APD
481  double y = p.y()/p.x()*dapd;
482 
483  LogDebug("EcalSim") << "Distance to APD: " << dapd
484  << " - y at APD: " << y;
485 
486  // Not straight: compute probability
487  if ( std::abs(y)>crwidth*0.5 ) {
488 
489  }
490 
491  // 2. Retrieve efficiency for this wavelength (in nm, from MeV)
492  double waveLength = p.mag()*1.239e8;
493 
494  energy = p.mag()*PMTResponse::getEfficiency(waveLength);
495 
496  LogDebug("EcalSim") << "Wavelength: " << waveLength << " - Energy: " << energy;
497 
498  return energy;
499 
500 }
#define LogDebug(id)
T getParameter(std::string const &) const
T getUntrackedParameter(std::string const &, T const &) const
const std::vector< double > & parameters(void) const
Give the parameters of the solid.
Definition: DDSolid.cc:144
const DDLogicalPart & logicalPart() const
The logical-part of the current node in the filtered-view.
const N & name() const
Definition: DDBase.h:74
bool readBothSide_
Definition: DreamSD.h:54
double getPhotonEnergyDeposit_(const G4ParticleMomentum &p, const G4ThreeVector &x, const G4Step *aStep)
Returns energy deposit for a given photon.
Definition: DreamSD.cc:459
std::pair< double, double > Doubles
Definition: DreamSD.h:32
Definition: CaloSD.h:37
Sin< T >::type sin(const T &t)
Definition: Sin.h:22
uint32_t setDetUnitId(const G4Step *) override
Definition: DreamSD.cc:98
Definition: weight.py:1
double birk1
Definition: DreamSD.h:55
bool setPbWO2MaterialProperties_(G4Material *aMaterial)
Sets material properties at run-time...
Definition: DreamSD.cc:397
double birk3
Definition: DreamSD.h:55
const DDSolid & solid(void) const
Returns a reference object of the solid being the shape of this LogicalPart.
void initRun() override
Definition: DreamSD.cc:78
Compact representation of the geometrical detector hierarchy.
Definition: DDCompactView.h:80
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e g
Definition: Activities.doc:4
A DDSolid represents the shape of a part.
Definition: DDSolid.h:39
const double MeV
static double getEfficiency(const double &waveLengthNm)
Return efficiency for given photon wavelength (in nm)
Definition: PMTResponse.cc:6
bool next()
set current node to the next node in the filtered tree
double cherenkovDeposit_(const G4Step *aStep)
Returns the total energy due to Cherenkov radiation.
Definition: DreamSD.cc:208
T sqrt(T t)
Definition: SSEVec.h:18
Cos< T >::type cos(const T &t)
Definition: Cos.h:22
int side
Definition: DreamSD.h:59
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
double getAverageNumberOfPhotons_(const double charge, const double beta, const G4Material *aMaterial, const G4MaterialPropertyVector *rIndex)
Returns average number of photons created by track.
Definition: DreamSD.cc:325
DDSolidShape shape(void) const
The type of the solid.
Definition: DDSolid.cc:138
double crystalWidth(G4LogicalVolume *) const
Definition: DreamSD.cc:195
double birk2
Definition: DreamSD.h:55
G4MaterialPropertiesTable * materialPropertiesTable
Definition: DreamSD.h:63
double curve_LY(const G4Step *, int)
Definition: DreamSD.cc:154
G4ThreeVector setToLocal(const G4ThreeVector &, const G4VTouchable *) const
Definition: CaloSD.cc:307
DimensionMap xtalLMap
Definition: DreamSD.h:57
double sd
double crystalLength(G4LogicalVolume *) const
Definition: DreamSD.cc:185
std::unique_ptr< G4PhysicsOrderedFreeVector > chAngleIntegrals_
Table of Cherenkov angle integrals vs photon momentum.
Definition: DreamSD.h:62
double getEnergyDeposit(const G4Step *) override
Definition: DreamSD.cc:59
double getAttenuation(const G4Step *aStep, double birk1, double birk2, double birk3) const
Definition: CaloSD.cc:462
bool doCherenkov_
Definition: DreamSD.h:54
double slopeLY
Definition: DreamSD.h:56
DreamSD(const std::string &, const DDCompactView &, const SensitiveDetectorCatalog &, edm::ParameterSet const &, const SimTrackManager *)
Definition: DreamSD.cc:27
bool firstChild()
set the current node to the first child ...
const std::string & name() const
Returns the name.
Definition: DDName.cc:53
void initMap(const std::string &, const DDCompactView &)
Definition: DreamSD.cc:109
bool useBirk
Definition: DreamSD.h:54