ATLAS Offline Software
Loading...
Searching...
No Matches
EnergyDepositionTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
5
6
12
16
18
19#include "CLHEP/Random/RandomEngine.h"
20#include "CLHEP/Random/RandExpZiggurat.h"
21#include "CLHEP/Random/RandFlat.h"
22#include "TLorentzVector.h"
23#include "CLHEP/Units/PhysicalConstants.h"
25#include <cmath>
26#include <fstream>
27#include <limits> //for numeric_limits min
28
29
30
31
32namespace{
33 //iBetaGamma function returning zero for the error case
34 double iBetaGammaFn(const double k){
35 double result(0.);
36 constexpr double me =ParticleConstants::electronMassInMeV; //electron mass in MeV, directly from CLHEP file
37 if (auto subCalc = 2. * me + k; subCalc>0){
38 result = std::sqrt(k*subCalc)/me;
39 }
40 return result;
41 }
42 double iBetaGammaFn(const TLorentzVector & vec){
43 double result(0.);
44 double beta = vec.Beta();
45 //if beta >=1, bad things happen because
46 //TLorentzVector::Gamma = 1./sqrt(1. - beta*beta)
47 if (beta <1.0) result = beta * vec.Gamma();
48 return result;
49 }
50 //=======================================
51 // TRF PDG
52 //=======================================
53 int
54 trfPDG(int pdgId) {
55 if (std::fabs(pdgId) == 2212) return 1; // proton
56
57 if (std::fabs(pdgId) == 211) return 2; // pion
58
59 // alpha is skipped -- 3
60 if (std::fabs(pdgId) == 11) return 4; // electron
61
62 if (std::fabs(pdgId) == 321) return 5; // kaon
63
64 if (std::fabs(pdgId) == 13) return 6; // muon
65
66 return -1; // unsupported particle
67 }
68
69 //==========================================
70 // S E T F A I L U R E
71 //==========================================
72 void
73 setFailureFlag(std::vector<std::pair<double, double> >& rawHitRecord) {
74 rawHitRecord.clear();
75 std::pair<double, double> specialFlag;
76 specialFlag.first = -1.;
77 specialFlag.second = -1.;
78 rawHitRecord.push_back(specialFlag);
79 }
80}
81
82// Constructor with parameters:
83EnergyDepositionTool::EnergyDepositionTool(const std::string& type, const std::string& name, const IInterface* parent) :
84 AthAlgTool(type, name, parent) {}
85
87
88//=======================================
89// I N I T I A L I Z E
90//=======================================
92 ATH_MSG_INFO("You are using EnergyDepositionTool for solid-state silicon detectors.");
93
94 ATH_CHECK(detStore()->retrieve(m_pixelID, "PixelID"));
95
96 //Setup distortions tool
98 ATH_MSG_DEBUG("Getting distortions tool");
99 ATH_CHECK(m_distortionKey.initialize());
100 }
101
102 if (m_doBichsel) {
103 // Load Bichsel data
104 m_bichselData.clear();
105 ATH_MSG_INFO("The number of collision for each sampling is " << m_nCols);
106 ATH_MSG_INFO("Loading data file");
107 int n_ParticleType = 6;
108 for (int iParticleType = 1; iParticleType <= n_ParticleType; iParticleType++) {
109 const std::string & inputFileName(PixelDigitization::formBichselDataFileName(iParticleType, m_nCols));
110 const std::string & fullFileName = PathResolverFindCalibFile(inputFileName);
111 ATH_MSG_INFO("Bichsel Data File "<<fullFileName);
113 m_bichselData.push_back(iData);
114 }
115 ATH_MSG_INFO("Finish Loading Data File");
116 }
117
118
119 m_doDeltaRay = (m_doBichsel && m_doDeltaRay); // if we don't do Bichsel model, no re-simulation on delta-ray at
120 // all!
121 return StatusCode::SUCCESS;
122}
123
124//=======================================
125// F I N A L I Z E
126//=======================================
128 ATH_MSG_DEBUG("EnergyDepositionTool::finalize()");
129 return StatusCode::SUCCESS;
130}
131
132//=======================================
133// D E P O S I T E N E R G Y
134//=======================================
136 std::vector<std::pair<double, double> >& trfHitRecord,
137 std::vector<double>& initialConditions,
138 CLHEP::HepRandomEngine* rndmEngine,
139 const EventContext &ctx) const {
140 ATH_MSG_DEBUG("Deposit energy in sensor volume.");
141
142 //Check if simulated particle or delta ray
143 const HepMcParticleLink McLink = HepMcParticleLink::getRedirectedLink(phit->particleLink(), phit.eventId(), ctx); // This link should now correctly resolve to the TruthEvent McEventCollection in the main StoreGateSvc.
144 HepMC::ConstGenParticlePtr genPart = McLink.cptr();
145 bool delta_hit = true;
146 if (genPart) delta_hit = false;
147 double sensorThickness = Module.design().thickness();
148
149
150 //Get path of particle through volume of G4
151 double stepsize = sensorThickness / m_numberOfSteps;
152 const CLHEP::Hep3Vector startPosition = phit->localStartPosition();
153 const CLHEP::Hep3Vector endPosition = phit->localEndPosition();
154
155 //Get entry and exit positions, store for SensorSim tools
156 double eta_0 = startPosition[SiHit::xEta];
157 double phi_0 = startPosition[SiHit::xPhi];
158 const double depth_0 = startPosition[SiHit::xDep];
159
160 double eta_f = endPosition[SiHit::xEta];
161 double phi_f = endPosition[SiHit::xPhi];
162 const double depth_f = endPosition[SiHit::xDep];
163
164 //Simulate effect of bowing on entry and exit points
165 if (!m_disableDistortions && !delta_hit) simulateBow(&Module, phi_0, eta_0, depth_0, phi_f, eta_f, depth_f);
166
167 double dEta = eta_f - eta_0;
168 double dPhi = phi_f - phi_0;
169 const double dDepth = depth_f - depth_0;
170 double pathLength = std::sqrt(dEta * dEta + dPhi * dPhi + dDepth * dDepth);
171
172 //Scale steps and charge chunks
173 const int nsteps = int(pathLength / stepsize) + 1;
174 const int ncharges = this->m_numberOfCharges * this->m_numberOfSteps / nsteps + 1;
175
176 //Store information
177 initialConditions.clear();
178 initialConditions = {eta_0, phi_0, depth_0, dEta, dPhi, dDepth, static_cast<double>(ncharges)};
179
181 // *** For Bichsel *** //
183 double iTotalLength = pathLength * 1000.; // mm -> micrometer
184 initialConditions.push_back(iTotalLength);
185
186 // -1 ParticleType means we are unable to run Bichel simulation for this case
187 int ParticleType = -1;
188 if (m_doBichsel and !(Module.isDBM()) and genPart) {
189 ParticleType = delta_hit ? (m_doDeltaRay ? 4 : -1) : trfPDG(genPart->pdg_id());
190 if (ParticleType != -1) { // this is a protection in case delta_hit == true (a delta ray)
191 TLorentzVector genPart_4V;
192
193 if (genPart) { // non-delta-ray
194 genPart_4V.SetPtEtaPhiM(genPart->momentum().perp(), genPart->momentum().eta(),
195 genPart->momentum().phi(), genPart->momentum().m());
196 if (iBetaGammaFn(genPart_4V) < m_doBichselBetaGammaCut){
197 ParticleType = -1;
198 }
199 } else { // delta-ray.
200 double k = phit->energyLoss() / CLHEP::MeV; // unit of MeV
201 if (iBetaGammaFn(k) < m_doBichselBetaGammaCut){
202 ParticleType = -1;
203 }
204 }
205
206 // In-time PU
207 if (!m_doPU) {
208 if (phit.eventId() != 0) ParticleType = -1;
209 }
210
211 // Out-of-time PU
212 // We don't cut on the out-of-time PU, since studies show that the fraction is too small
213 }
214 }
215
216 if (ParticleType != -1) { // yes, good to go with Bichsel
217 // I don't know why genPart->momentum() goes crazy ...
218 TLorentzVector genPart_4V;
219 double iBetaGamma=0.;
220 if (genPart) {
221 genPart_4V.SetPtEtaPhiM(genPart->momentum().perp(), genPart->momentum().eta(),
222 genPart->momentum().phi(), genPart->momentum().m());
223 iBetaGamma = iBetaGammaFn(genPart_4V);
224 } else {
225 double k = phit->energyLoss() / CLHEP::MeV; // unit of MeV // unit of MeV
226 iBetaGamma = iBetaGammaFn(k);
227 }
228
229 int iParticleType = ParticleType;
230 // begin simulation
231 std::vector<std::pair<double, double> > rawHitRecord = bichselSim(iBetaGamma, iParticleType, iTotalLength,
232 genPart ? (genPart->momentum().e() /
233 CLHEP::MeV) : (phit->energyLoss() /
234 CLHEP::MeV),
235 rndmEngine);
236
237 // check if returned simulation result makes sense
238 if (rawHitRecord.empty()) { // deal with rawHitRecord==0 specifically -- no energy deposition
239 std::pair<double, double> specialHit;
240 specialHit.first = 0.;
241 specialHit.second = 0.;
242 trfHitRecord.push_back(specialHit);
243 } else if ((rawHitRecord.size() == 1) && (rawHitRecord[0].first == -1.) && (rawHitRecord[0].second == -1.)) { // special flag returned from bichselSim meaning it FAILs
244 for (int j = 0; j < nsteps; j++) { // do the same thing as old digitization method
245 std::pair<double, double> specialHit;
246 specialHit.first = 1.0 * iTotalLength / nsteps * (j + 0.5);
247 specialHit.second = phit->energyLoss() * 1.E+6 / nsteps;
248 trfHitRecord.push_back(specialHit);
249 }
250 } else { // cluster thousands hits to ~20 groups
251 trfHitRecord = clusterHits(rawHitRecord, nsteps);
252 }
253 } else { // same as old digitization method
255 // *** B I C H S E L O F F *** //
257 //double iTotalLength = pathLength*1000.; // mm -> micrometer
258 for (int j = 0; j < nsteps; j++) { // do the same thing as old digitization method
259 std::pair<double, double> specialHit;
260 specialHit.first = 1.0 * iTotalLength / nsteps * (j + 0.5);
261 specialHit.second = phit->energyLoss() * 1.E+6 / nsteps;
262 trfHitRecord.push_back(specialHit);
263 }
264 }
265
266 // *** Finsih Bichsel *** //
267 return StatusCode::SUCCESS;
268}
269
270//======================================
271// S I M U L A T E B O W
272//======================================
273void EnergyDepositionTool::simulateBow(const InDetDD::SiDetectorElement* element, double& xi, double& yi,
274 const double zi, double& xf, double& yf, const double zf) const {
275 // If tool is NONE we apply no correction.
276 Amg::Vector3D dir(element->hitPhiDirection() * (xf - xi),
277 element->hitEtaDirection() * (yf - yi), element->hitDepthDirection() * (zf - zi));
278
279 Amg::Vector2D locposi = element->hitLocalToLocal(yi, xi);
280 Amg::Vector2D locposf = element->hitLocalToLocal(yf, xf);
281
282 Amg::Vector2D newLocposi = SG::ReadCondHandle<PixelDistortionData>(m_distortionKey)->correctSimulation(m_pixelID->wafer_hash(
283 element->
284 identify()), locposi,
285 dir);
286 Amg::Vector2D newLocposf = SG::ReadCondHandle<PixelDistortionData>(m_distortionKey)->correctSimulation(m_pixelID->wafer_hash(
287 element->
288 identify()), locposf,
289 dir);
290
291 // Extract new coordinates and convert back to hit frame.
292 xi = newLocposi[Trk::x] * element->hitPhiDirection();
293 yi = newLocposi[Trk::y] * element->hitEtaDirection();
294
295 xf = newLocposf[Trk::x] * element->hitPhiDirection();
296 yf = newLocposf[Trk::y] * element->hitEtaDirection();
297}
298
299//=======================================
300// B I C H S E L D E P O S I T I O N
301//=======================================
302// input total length should be in the unit of micrometer
303// InciEnergy should be in MeV
304// In case there is any abnormal in runtime, (-1,-1) will be returned indicating old deposition model should be used
305// instead
306//-----------------------------------------------------------
307std::vector<std::pair<double, double> >
308EnergyDepositionTool::bichselSim(double BetaGamma, int ParticleType,double TotalLength,
309 double InciEnergy,CLHEP::HepRandomEngine* rndmEngine) const {
310 ATH_MSG_DEBUG("Begin EnergyDepositionTool::bichselSim");
311
312 // prepare hit record (output)
313 std::vector<std::pair<double, double> > rawHitRecord;
314 double TotalEnergyLoss = 0.;
315 double accumLength = 0.;
316
317 if (BetaGamma <= std::numeric_limits<double>::min()){
318 setFailureFlag(rawHitRecord);
319 return rawHitRecord;
320 }
321
322 // load relevant data
323 const BichselData& iData = m_bichselData[ParticleType - 1];
324 double BetaGammaLog10 = std::log10(BetaGamma);
325 std::pair<int, int> indices_BetaGammaLog10 = iData.getBetaGammaIndices(BetaGammaLog10);
326
327 // upper bound
328 double IntXUpperBound = iData.interpolateCrossSection(indices_BetaGammaLog10, BetaGammaLog10);
329 if (IntXUpperBound <= 0.) {
330 ATH_MSG_WARNING("Negative IntXUpperBound in EnergyDepositionTool::bichselSim! (-1,-1) will be returned for log(betaGamma) = "<<BetaGammaLog10);
331 setFailureFlag(rawHitRecord);
332 return rawHitRecord;
333 }
334
335 if(IntXUpperBound<std::numeric_limits<double>::min()){
336 setFailureFlag(rawHitRecord);
337 return rawHitRecord;
338 }
339
340 // mean-free path
341 double lambda = (1. / IntXUpperBound) * 1.E4; // unit of IntX is cm-1. It needs to be converted to micrometer-1
342
343
344 // direct those hits with potential too many steps into nominal simulation
345 int LoopLimit = m_LoopLimit; // limit assuming 1 collision per sampling
346 if (std::abs(1.0 * TotalLength / lambda) > LoopLimit) { // m_nCols is cancelled out in the formula
347 setFailureFlag(rawHitRecord);
348 return rawHitRecord;
349 }
350
351 // begin simulation
352 int count = 0;
353 while (true) {
354 // infinite loop protection
355 if (count >= (1.0 * LoopLimit / m_nCols)) {
357 "Potential infinite loop in bichselSim. Exit Loop. A special flag "
358 "will be returned (-1,-1). The total length is "
359 << TotalLength << ". The lambda is " << lambda << ".");
360 setFailureFlag(rawHitRecord);
361 break;
362 }
363
364 // sample hit position -- exponential distribution
365 double HitPosition = 0.;
366 for (int iHit = 0; iHit < m_nCols; iHit++) {
367 HitPosition += CLHEP::RandExpZiggurat::shoot(rndmEngine, lambda);
368 }
369 // termination by hit position
370 // yes, in case m_nCols > 1, we will loose the last m_nCols collisions. So m_nCols cannot be too big
371 if (accumLength + HitPosition >= TotalLength) break;
372
373 // sample single collision
374 double TossEnergyLoss = -1.;
375 while (TossEnergyLoss <= 0.) { // we have to do this because sometimes TossEnergyLoss will be negative due to too
376 // small TossIntX
377 double TossIntX = CLHEP::RandFlat::shoot(rndmEngine, 0., IntXUpperBound);
378 TossEnergyLoss = iData.interpolateCollisionEnergy(indices_BetaGammaLog10, std::log10(TossIntX));
379 }
380
381 // check if it is delta-ray -- delta-ray is already taken care of by G4 and treated as an independent hit.
382 // Unfortunately, we won't deal with delta-ray using Bichsel's model
383 // as long as m_nCols is not very big, the probability of having >= 2 such a big energy loss in a row is very small.
384 // In case there is a delta-ray, it would be so dominant that other energy deposition becomes negligible
385 if (TossEnergyLoss > (m_DeltaRayCut * 1000.)) {
386 TossEnergyLoss = 0.;
387 }
388
389 bool fLastStep = false;
390
391 if (((TotalEnergyLoss + TossEnergyLoss) / 1.E+6) > InciEnergy) {
393 "Energy loss is larger than incident energy in EnergyDepositionTool::bichselSim! This is usually delta-ray.");
394 TossEnergyLoss = InciEnergy * 1.E+6 - TotalEnergyLoss;
395 fLastStep = true;
396 }
397
398 // update
399 accumLength += HitPosition;
400 TotalEnergyLoss += TossEnergyLoss;
401
402 // record this hit
403 std::pair<double, double> oneHit;
404 if (m_nCols == 1) oneHit.first = accumLength;
405 else oneHit.first = (accumLength - 1.0 * HitPosition / 2);// as long as m_nCols is small enough (making sure lambda*m_nCols is within resolution of a pixel), then taking middle point might still be reasonable
406 oneHit.second = TossEnergyLoss;
407 rawHitRecord.push_back(oneHit);
408
409 count++;
410
411 if (fLastStep) break;
412 }
413
414 ATH_MSG_DEBUG("Finish EnergyDepositionTool::bichselSim");
415
416 return rawHitRecord;
417}
418
419//=======================================
420// C L U S T E R H I T S
421//=======================================
422std::vector<std::pair<double, double> > EnergyDepositionTool::clusterHits(std::vector<std::pair<double,
423 double> >& rawHitRecord,
424 int n_pieces) const {
425 ATH_MSG_DEBUG("Begin EnergyDepositionTool::clusterHits");
426 std::vector<std::pair<double, double> > trfHitRecord;
427
428 if (static_cast<int>(rawHitRecord.size()) < n_pieces) { // each single collision is the most fundamental unit
429 n_pieces = rawHitRecord.size();
430 }
431
432 int unitlength = int(1.0 * rawHitRecord.size() / n_pieces);
433 int index_start = 0;
434 int index_end = unitlength - 1; // [index_start, index_end] are included
435 while (true) {
436 // calculate weighted center of each slice
437 double position = 0.;
438 double energyloss = 0.;
439
440 for (int index = index_start; index <= index_end; index++) {
441 position += (rawHitRecord[index].first * rawHitRecord[index].second);
442 energyloss += rawHitRecord[index].second;
443 }
444 position = (energyloss == 0. ? 0. : position / energyloss);
445
446 // store
447 std::pair<double, double> oneHit;
448 oneHit.first = position;
449 oneHit.second = energyloss;
450 trfHitRecord.push_back(oneHit);
451
452 // procede to next slice
453 index_start = index_end + 1;
454 index_end = index_start + unitlength - 1;
455
456 if (index_start > static_cast<int>(rawHitRecord.size() - 1)) {
457 break;
458 }
459
460 if (index_end > static_cast<int>(rawHitRecord.size() - 1)) {
461 index_end = rawHitRecord.size() - 1;
462 }
463 }
464
465 ATH_MSG_DEBUG("Finish EnergyDepositionTool::clusterHits");
466
467 return trfHitRecord;
468}
469
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
std::vector< size_t > vec
A number of constexpr particle constants to avoid hardcoding them directly in various places.
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
This is an Identifier helper class for the Pixel subdetector.
ParticleType
Definition TruthClasses.h:8
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
const ServiceHandle< StoreGateSvc > & detStore() const
Gaudi::Property< bool > m_doDeltaRay
Gaudi::Property< bool > m_doPU
Gaudi::Property< bool > m_disableDistortions
Gaudi::Property< double > m_doBichselBetaGammaCut
Gaudi::Property< int > m_numberOfCharges
virtual StatusCode depositEnergy(const TimedHitPtr< SiHit > &phit, const InDetDD::SiDetectorElement &Module, std::vector< std::pair< double, double > > &trfHitRecord, std::vector< double > &initialConditions, CLHEP::HepRandomEngine *rndmEngine, const EventContext &ctx) const
Gaudi::Property< bool > m_doBichsel
virtual StatusCode finalize()
void simulateBow(const InDetDD::SiDetectorElement *element, double &xi, double &yi, const double zi, double &xf, double &yf, const double zf) const
std::vector< std::pair< double, double > > clusterHits(std::vector< std::pair< double, double > > &rawHitRecord, int n_pieces) const
Gaudi::Property< double > m_DeltaRayCut
Gaudi::Property< int > m_nCols
virtual ~EnergyDepositionTool()
Gaudi::Property< int > m_LoopLimit
SG::ReadCondHandleKey< PixelDistortionData > m_distortionKey
virtual StatusCode initialize()
std::vector< BichselData > m_bichselData
Gaudi::Property< int > m_numberOfSteps
std::vector< std::pair< double, double > > bichselSim(double BetaGamma, int ParticleType, double TotalLength, double InciEnergy, CLHEP::HepRandomEngine *rndmEngine) const
double thickness() const
Method which returns thickness of the silicon wafer.
Class to hold geometrical description of a silicon detector element.
virtual const SiDetectorDesign & design() const override final
access to the local description (inline):
double hitPhiDirection() const
See previous method.
double hitDepthDirection() const
Directions of hit depth,phi,eta axes relative to reconstruction local position axes (LocalPosition).
double hitEtaDirection() const
See previous method.
Amg::Vector2D hitLocalToLocal(double xEta, double xPhi) const
Simulation/Hit local frame to reconstruction local frame.
@ xPhi
Definition SiHit.h:162
@ xEta
Definition SiHit.h:162
@ xDep
Definition SiHit.h:162
a smart pointer to a hit that also provides access to the extended timing info of the host event.
Definition TimedHitPtr.h:18
unsigned short eventId() const
the index of the component event in PileUpEventInfo.
Definition TimedHitPtr.h:47
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:146
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
const GenParticle * ConstGenParticlePtr
Definition GenParticle.h:38
constexpr double electronMassInMeV
the mass of the electron (in MeV)
std::string formBichselDataFileName(int particleType, unsigned int nCols)
BichselData getBichselDataFromFile(const std::string &fullFilename)
@ x
Definition ParamDefs.h:55
@ y
Definition ParamDefs.h:56
Definition index.py:1
double interpolateCrossSection(std::pair< int, int > indices_BetaGammaLog10, double BetaGammaLog10) const
std::pair< int, int > getBetaGammaIndices(double BetaGammaLog10) const
double interpolateCollisionEnergy(std::pair< int, int > indices_BetaGammaLog10, double IntXLog10) const