16 #include "CLHEP/Random/RandFlat.h"
17 #include "CLHEP/Random/RandGaussZiggurat.h"
19 #include "CLHEP/Units/PhysicalConstants.h"
22 #include "GaudiKernel/SystemOfUnits.h"
58 std::vector<std::string> mapsPath_list;
59 std::vector<std::string> TCADpath_list;
67 iblFiles, sensorFiles, sensorFiles, sensorFiles
72 ATH_MSG_INFO(
"Use interpolation, but the input map/fluence/valtage are not set.");
73 return StatusCode::FAILURE;
75 ATH_MSG_INFO(
"No benchmark value set for fluence. Use interpolation.");
77 mapsPath_list.clear();
88 for (
unsigned int i = 0;
i < mapsPath_list.size();
i++) {
89 ATH_MSG_INFO(
"Using maps located in: " << mapsPath_list.at(
i) <<
" for layer No." <<
i);
90 ATH_MSG_INFO(
"Create E field via interpolation based on files from: " << TCADpath_list.at(
i));
91 std::unique_ptr<TFile> mapsFile(TFile::Open((mapsPath_list.at(
i)).c_str(),
"READ"));
94 return StatusCode::FAILURE;
98 std::unique_ptr<TH3F> ramoPotentialMap_hold(mapsFile->Get<TH3F>(
"hramomap1"));
99 if (!ramoPotentialMap_hold) {
100 ramoPotentialMap_hold.reset(mapsFile->Get<TH3F>(
"ramo3d"));
101 ATH_MSG_INFO(
"Did not find a Ramo potential map. Will use an approximate form.");
103 if (!ramoPotentialMap_hold) {
105 return StatusCode::FAILURE;
107 ramoPotentialMap_hold->SetDirectory(
nullptr);
111 TH1F* eFieldMap_hold(
nullptr);
113 TCADpath_list.at(
i),
true));
115 eFieldMap_hold->SetDirectory(
nullptr);
117 TH2F* lorentzMap_e_hold(
nullptr);
118 TH2F* lorentzMap_h_hold(
nullptr);
119 TH2F* distanceMap_h_hold(
nullptr);
120 TH2F* distanceMap_e_hold(
nullptr);
121 TH1F* timeMap_e_hold(
nullptr);
122 TH1F* timeMap_h_hold(
nullptr);
125 timeMap_h_hold, lorentzMap_e_hold, lorentzMap_h_hold,
126 eFieldMap_hold,
nullptr));
130 TString prename =
"map_layer_";
132 prename +=
"distance_e.root";
133 distanceMap_e_hold->SaveAs(prename,
"");
134 prename.ReplaceAll(
"_e",
"_h");
135 distanceMap_h_hold->SaveAs(prename,
"");
136 prename.ReplaceAll(
"distance",
"time");
137 timeMap_h_hold->SaveAs(prename,
"");
138 prename.ReplaceAll(
"_h",
"_e");
139 timeMap_e_hold->SaveAs(prename,
"");
140 prename.ReplaceAll(
"time",
"lorentz");
141 lorentzMap_e_hold->SaveAs(prename,
"");
142 prename.ReplaceAll(
"_e",
"_h");
143 lorentzMap_h_hold->SaveAs(prename,
"");
146 if (!distanceMap_e_hold || !distanceMap_h_hold || !timeMap_e_hold || !timeMap_h_hold ||
147 !lorentzMap_e_hold || !lorentzMap_h_hold) {
148 ATH_MSG_ERROR(
"Unable to load at least one of the distance/time/Lorentz angle maps.");
149 return StatusCode::FAILURE;
152 lorentzMap_e_hold->SetDirectory(
nullptr);
153 lorentzMap_h_hold->SetDirectory(
nullptr);
154 distanceMap_e_hold->SetDirectory(
nullptr);
155 distanceMap_h_hold->SetDirectory(
nullptr);
156 timeMap_e_hold->SetDirectory(
nullptr);
157 timeMap_h_hold->SetDirectory(
nullptr);
168 delete eFieldMap_hold;
169 delete lorentzMap_e_hold;
170 delete lorentzMap_h_hold;
171 delete distanceMap_e_hold;
172 delete distanceMap_h_hold;
173 delete timeMap_e_hold;
174 delete timeMap_h_hold;
183 constexpr std::size_t numberOfLayers = 4;
189 return StatusCode::FAILURE;
194 return StatusCode::FAILURE;
199 return StatusCode::FAILURE;
204 return StatusCode::FAILURE;
208 ATH_MSG_INFO(
"Reading histogram: " <<
i <<
" for Lorentz angle correction needed for Pixel radiation damage simulation");
209 std::unique_ptr<TH1>
h(
file->Get<TH1>(
i.c_str()));
211 ATH_MSG_ERROR(
"Cannot read histogram " <<
i <<
" needed for Lorentz angle correction");
212 return StatusCode::FAILURE;
220 ATH_MSG_INFO(
"Reading histogram: " <<
i <<
" for charge correction needed for Pixel radiation damage simulation");
221 std::unique_ptr<TH1>
h(
file->Get<TH1>(
i.c_str()));
223 ATH_MSG_ERROR(
"Cannot read histogram " <<
i <<
" needed for charge correction");
224 return StatusCode::FAILURE;
232 ATH_MSG_INFO(
"Reading histogram: " <<
i <<
" for distance correction needed for Pixel radiation damage simulation");
233 std::unique_ptr<TH1>
h(
file->Get<TH1>(
i.c_str()));
235 ATH_MSG_ERROR(
"Cannot read histogram " <<
i <<
" needed for distance correction");
236 return StatusCode::FAILURE;
246 return StatusCode::SUCCESS;
254 return StatusCode::SUCCESS;
264 std::vector< std::pair<double, double> >& trfHitRecord,
265 std::vector<double>& initialConditions,
266 CLHEP::HepRandomEngine* rndmEngine,
267 const EventContext &ctx) {
273 return StatusCode::SUCCESS;
278 if (!Module.
isDBM()) {
279 return StatusCode::SUCCESS;
288 double eta_0 = initialConditions[0];
289 double phi_0 = initialConditions[1];
290 double depth_0 = initialConditions[2];
291 double dEta = initialConditions[3];
292 double dPhi = initialConditions[4];
293 double dDepth = initialConditions[5];
294 double ncharges = initialConditions[6];
295 double iTotalLength = initialConditions[7];
297 const double oneOverNchargesTimes1e6 = 1./(1.E+6 * ncharges);
304 int etaCells = p_design.
columns();
305 int phiCells = p_design.
rows();
307 double eleholePairEnergy = 0;
308 double smearRand = 0;
310 if (Module.
isDBM()) {
311 eleholePairEnergy = 1. / (13. *
CLHEP::eV);
313 smearRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
320 double smearScale = 1. + 0.35 * smearRand;
321 double tanLorentz(0);
325 coLorentz = std::sqrt(1.0 + (tanLorentz*tanLorentz));
333 const double pHitTime =
hitTime(phit);
335 const double halfEtaPitch = 0.5*Module.
etaPitch();
336 const double halfPhiPitch = 0.5*Module.
phiPitch();
342 std::pair<double, double> trappingTimes;
356 std::map<unsigned, std::pair<SiLocalPosition, double>> cachedChargeMap;
357 std::map<unsigned, SiCellId> diodeCellMap;
358 for (
const auto& iHitRecord : trfHitRecord) {
360 double eta_i = eta_0;
361 double phi_i = phi_0;
362 double depth_i = depth_0;
364 eta_i += 1.0 * iHitRecord.first / iTotalLength *
dEta;
365 phi_i += 1.0 * iHitRecord.first / iTotalLength *
dPhi;
366 depth_i += 1.0 * iHitRecord.first / iTotalLength * dDepth;
372 if (!pixel_i.
isValid())
continue;
376 double dist_electrode = 0.5 * sensorThickness - Module.
design().
readoutSide() * depth_i;
377 if (dist_electrode < 0) {
385 const int nnLoop_pixelEtaMin =
std::max(-1, pixel_i.
etaIndex() + 1 - etaCells);
387 const int nnLoop_pixelPhiMin =
std::max(-1, pixel_i.
phiIndex() + 1 - phiCells);
389 std::array<double, 3> sensorScales{};
391 const std::size_t distance_f_e_bin_x = distanceMap_e.
getBinX(dist_electrode);
392 const std::size_t distance_f_h_bin_x = distanceMap_h.
getBinX(dist_electrode);
393 const std::size_t tanLorentz_e_bin_x = lorentzMap_e.
getBinX(dist_electrode);
394 const std::size_t tanLorentz_h_bin_x = lorentzMap_h.
getBinX(dist_electrode);
396 const std::size_t sizePhi = std::abs(nnLoop_pixelPhiMax - nnLoop_pixelPhiMin) + 1;
398 const auto pixel_eta = pixel_i.
etaIndex();
399 const auto pixel_phi = pixel_i.
phiIndex();
401 for (
int p = nnLoop_pixelEtaMin;
p <= nnLoop_pixelEtaMax;
p++) {
402 const std::size_t ieta =
p - nnLoop_pixelEtaMin;
406 if (std::abs(columnWidth - 0.6) < 1
e-9) {
408 }
else if (std::abs(columnWidth - 0.45) < 1
e-9) {
410 }
else if (std::abs(columnWidth - 0.5) < 1
e-9) {
413 sensorScales[ieta] = scale_f;
415 for (
int q = nnLoop_pixelPhiMin;
q <= nnLoop_pixelPhiMax;
q++) {
418 const std::size_t iphi =
q - nnLoop_pixelPhiMin;
419 const std::size_t
index = iphi + ieta*sizePhi;
426 for (
int j = 0; j < ncharges; j++) {
428 double energy_per_step = oneOverNchargesTimes1e6 * iHitRecord.second;
429 double u = CLHEP::RandFlat::shoot(rndmEngine, 0.0, 1.0);
431 const double drifttime_e = (-1.) * (trappingTimes.first) * logf(
u);
432 u = CLHEP::RandFlat::shoot(rndmEngine, 0.0, 1.0);
433 const double drifttime_h = (-1.) * (trappingTimes.second) * logf(
u);
441 const double depth_f_e = distanceMap_e.
getContent(distance_f_e_bin_x, distanceMap_e.
getBinY(drifttime_e));
442 const double depth_f_h = distanceMap_h.
getContent(distance_f_h_bin_x, distanceMap_h.
getBinY(drifttime_h));
443 const double tanLorentz_e = lorentzMap_e.
getContent(tanLorentz_e_bin_x, lorentzMap_e.
getBinY(depth_f_e));
444 const double tanLorentz_h = lorentzMap_h.
getContent(tanLorentz_h_bin_x, lorentzMap_h.
getBinY(depth_f_h));
445 const double dz_e = std::abs(dist_electrode - depth_f_e);
446 const double dz_h = std::abs(depth_f_h - dist_electrode);
447 const double coLorentz_e = std::sqrt(1.0 + (tanLorentz_e*tanLorentz_e));
450 double phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
454 const double phi_f_e = phi_i + dz_e * tanLorentz_e + rdif_e * phiRand;
455 double etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
456 double eta_f_e = eta_i + rdif_e * etaRand;
458 phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
459 const double coLorentz_h = std::sqrt(1.0 + (tanLorentz_h*tanLorentz_h));
461 const double phi_f_h = phi_i + dz_h * tanLorentz_h + rdif_h * phiRand;
462 etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
463 double eta_f_h = eta_i + rdif_h * etaRand;
472 const std::size_t ramo_f_e_bin_z = ramoPotentialMap.
getBinZ(1
e3*depth_f_e);
473 const std::size_t ramo_f_h_bin_z = ramoPotentialMap.
getBinZ(1
e3*depth_f_h);
475 const bool isFirstZ_e = ramoPotentialMap.
isFirstZ(1
e3*depth_f_e);
476 const bool isOverflowZ_h = ramoPotentialMap.
isOverflowZ(1
e3*depth_f_h);
478 const double pixelEta_f_e = eta_f_e - centreOfPixel_i.
xEta();
479 const double pixelPhi_f_e = phi_f_e - centreOfPixel_i.
xPhi();
481 const double pixelEta_f_h = eta_f_h - centreOfPixel_i.
xEta();
482 const double pixelPhi_f_h = phi_f_h - centreOfPixel_i.
xPhi();
486 for (
int p = nnLoop_pixelEtaMin;
p <= nnLoop_pixelEtaMax;
p++) {
487 const std::size_t ieta =
p - nnLoop_pixelEtaMin;
489 for (
int q = nnLoop_pixelPhiMin;
q <= nnLoop_pixelPhiMax;
q++) {
493 const std::size_t iphi =
q - nnLoop_pixelPhiMin;
494 const std::size_t
index = iphi + ieta*sizePhi;
498 const double dPhi_nn_centre = centrePixelNN.second - centreOfPixel_i.
xPhi();
499 const double dEta_nn_centre = centrePixelNN.first - centreOfPixel_i.
xEta();
507 const double dEta_f_e = std::abs(pixelEta_f_e - dEta_nn_centre)*sensorScales[ieta];
508 const double dPhi_f_e = std::abs(pixelPhi_f_e - dPhi_nn_centre);
509 const double dEta_f_h = 1
e3*std::abs(pixelEta_f_h - dEta_nn_centre)*sensorScales[ieta];
510 const double dPhi_f_h = 1
e3*std::abs(pixelPhi_f_h - dPhi_nn_centre);
513 double ramo_f_e = 0.0;
514 double ramo_f_h = 0.0;
517 if (dEta_f_e >= halfEtaPitch || dPhi_f_e >= halfPhiPitch) {
530 ramo_f_h = ramoPotentialMap.
getContent(ramoPotentialMap.
getBinX(dPhi_f_h), ramoPotentialMap.
getBinY(dEta_f_h), ramo_f_h_bin_z);
536 const double potentialDiff = ramo_f_e - ramo_f_h;
538 const double induced_charge = potentialDiff * energy_per_step * eleholePairEnergy;
540 unsigned key = (
static_cast<unsigned>(pixel_eta-
p) << 16) |
static_cast<unsigned>(pixel_phi-
q);
541 auto cacheIterator = cachedChargeMap.find(
key);
542 if(cacheIterator == cachedChargeMap.end()) {
543 cachedChargeMap.insert(std::make_pair(
key, std::make_pair(Module.
hitLocalToLocal(centrePixelNN.first, centrePixelNN.second), induced_charge)));
545 cacheIterator->second.second += induced_charge;
552 std::for_each(cachedChargeMap.begin(), cachedChargeMap.end(), [&diodeCellMap, &Module, &chargedDiodes, &pHitTime, &particleLink](
auto& pos_charge_pair){
553 auto& key = pos_charge_pair.first;
554 auto& chargePos = pos_charge_pair.second.first;
555 auto& charge_value = pos_charge_pair.second.second;
557 const SiSurfaceCharge scharge(chargePos, SiCharge(charge_value, pHitTime, SiCharge::track, particleLink));
558 auto diodeIterator = diodeCellMap.find(key);
559 if(diodeIterator == diodeCellMap.end()) diodeIterator = diodeCellMap.insert(std::make_pair(key, Module.cellIdOfPosition(scharge.position()))).first;
560 const SiCellId& thisDiode = diodeIterator->second;
561 if (thisDiode.isValid()) {
562 const SiCharge& charge = scharge.charge();
563 chargedDiodes.add(thisDiode, charge);
568 else if (m_radiationDamageSimulationType == RadiationDamageSimulationType::TEMPLATE_CORRECTION && !(Module.isDBM()) && Module.isBarrel()){
574 int layerToRead = isITk && m_digitizeITk3Das3D ?
layer - 1 :
layer;
577 if (layerToRead > 3) {
585 for (
const auto & iHitRecord : trfHitRecord) {
586 double eta_i = eta_0;
587 double phi_i = phi_0;
588 double depth_i = depth_0;
590 eta_i += 1.0 * iHitRecord.first / iTotalLength *
dEta;
591 phi_i += 1.0 * iHitRecord.first / iTotalLength *
dPhi;
592 depth_i += 1.0 * iHitRecord.first / iTotalLength * dDepth;
600 tanLorentz = lorentzCorrectionHist.
getContent(depthZ);
601 coLorentz = std::sqrt(1.0 + (tanLorentz*tanLorentz));
604 const double chargeCorrection = chargeCorrectionHist.
getContent(depthZ);
607 double nontrappingProbability = 1.0;
608 if (Module.isDBM()) {
609 nontrappingProbability =
exp(-dist_electrode / collectionDist);
612 for (
int j = 0; j < ncharges; j++) {
614 double energy_per_step = oneOverNchargesTimes1e6 * iHitRecord.second;
616 double rdif = this->m_diffusionConstant * std::sqrt(dist_electrode * coLorentz / 0.3);
619 double phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
620 double phi_drifted = phi_i + dist_electrode * tanLorentz + rdif * phiRand;
621 double etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
622 double eta_drifted = eta_i + rdif * etaRand;
626 applyIBLSlimEdges(energy_per_step, eta_drifted);
630 const SiLocalPosition& chargePos = Module.hitLocalToLocal(eta_drifted, phi_drifted);
634 if (Module.isDBM()) {
635 ed = energy_per_step * eleholePairEnergy * nontrappingProbability * smearScale;
637 ed = energy_per_step * eleholePairEnergy;
640 ed *= chargeCorrection;
645 const SiCellId& diode = Module.cellIdOfPosition(scharge.position());
649 chargedDiodes.add(diode,
charge);
654 for (
const auto & iHitRecord : trfHitRecord) {
655 double eta_i = eta_0;
656 double phi_i = phi_0;
657 double depth_i = depth_0;
659 eta_i += 1.0 * iHitRecord.first / iTotalLength *
dEta;
660 phi_i += 1.0 * iHitRecord.first / iTotalLength *
dPhi;
661 depth_i += 1.0 * iHitRecord.first / iTotalLength * dDepth;
666 double dist_electrode = 0.5 * sensorThickness - Module.design().readoutSide() * depth_i;
667 if (dist_electrode < 0) {
672 double nontrappingProbability = 1.0;
673 if (Module.isDBM()) {
674 nontrappingProbability =
exp(-dist_electrode / collectionDist);
677 for (
int j = 0; j < ncharges; j++) {
679 double energy_per_step = oneOverNchargesTimes1e6 * iHitRecord.second;
681 double rdif = this->m_diffusionConstant * std::sqrt(dist_electrode * coLorentz / 0.3);
684 double phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
685 double phi_drifted = phi_i + dist_electrode * tanLorentz + rdif * phiRand;
686 double etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
687 double eta_drifted = eta_i + rdif * etaRand;
691 applyIBLSlimEdges(energy_per_step, eta_drifted);
695 const SiLocalPosition& chargePos = Module.hitLocalToLocal(eta_drifted, phi_drifted);
699 if (Module.isDBM()) {
700 ed = energy_per_step * eleholePairEnergy * nontrappingProbability * smearScale;
702 ed = energy_per_step * eleholePairEnergy;
708 const SiCellId& diode = Module.cellIdOfPosition(scharge.position());
712 chargedDiodes.add(diode,
charge);
718 return StatusCode::SUCCESS;
722 if (std::abs(eta_drifted) > 20.440) {
723 energy_per_step = 0.0;
725 if (std::abs(eta_drifted) < 20.440 && std::abs(eta_drifted) > 20.200) {
726 if (eta_drifted > 0) {
727 energy_per_step = energy_per_step * (68.13 - eta_drifted * 3.333);
728 eta_drifted = eta_drifted - 0.250;
730 energy_per_step = energy_per_step * (68.13 + eta_drifted * 3.333);
731 eta_drifted = eta_drifted + 0.250;
734 if (std::abs(eta_drifted) < 20.200 && std::abs(eta_drifted) > 20.100) {
735 if (eta_drifted > 0) {
736 energy_per_step = energy_per_step * (41.2 - eta_drifted * 2.0);
737 eta_drifted = eta_drifted - 0.250;
739 energy_per_step = energy_per_step * (41.2 + eta_drifted * 2.0);
740 eta_drifted = eta_drifted + 0.250;