15 #include "CLHEP/Random/RandFlat.h"
16 #include "CLHEP/Random/RandGaussZiggurat.h"
18 #include "CLHEP/Units/PhysicalConstants.h"
21 #include "GaudiKernel/SystemOfUnits.h"
53 std::vector<std::string> mapsPath_list;
54 std::vector<std::string> TCADpath_list;
62 iblFiles, sensorFiles, sensorFiles, sensorFiles
67 ATH_MSG_INFO(
"Use interpolation, but the input map/fluence/valtage are not set.");
68 return StatusCode::FAILURE;
70 ATH_MSG_INFO(
"No benchmark value set for fluence. Use interpolation.");
72 mapsPath_list.clear();
83 for (
unsigned int i = 0;
i < mapsPath_list.size();
i++) {
84 ATH_MSG_INFO(
"Using maps located in: " << mapsPath_list.at(
i) <<
" for layer No." <<
i);
85 ATH_MSG_INFO(
"Create E field via interpolation based on files from: " << TCADpath_list.at(
i));
86 std::unique_ptr<TFile> mapsFile(TFile::Open((mapsPath_list.at(
i)).c_str(),
"READ"));
89 return StatusCode::FAILURE;
93 std::unique_ptr<TH3F> ramoPotentialMap_hold(mapsFile->Get<TH3F>(
"hramomap1"));
94 if (!ramoPotentialMap_hold) {
95 ramoPotentialMap_hold.reset(mapsFile->Get<TH3F>(
"ramo3d"));
96 ATH_MSG_INFO(
"Did not find a Ramo potential map. Will use an approximate form.");
98 if (!ramoPotentialMap_hold) {
100 return StatusCode::FAILURE;
102 ramoPotentialMap_hold->SetDirectory(
nullptr);
106 TH1F* eFieldMap_hold(
nullptr);
108 TCADpath_list.at(
i),
true));
110 eFieldMap_hold->SetDirectory(
nullptr);
112 TH2F* lorentzMap_e_hold(
nullptr);
113 TH2F* lorentzMap_h_hold(
nullptr);
114 TH2F* distanceMap_h_hold(
nullptr);
115 TH2F* distanceMap_e_hold(
nullptr);
116 TH1F* timeMap_e_hold(
nullptr);
117 TH1F* timeMap_h_hold(
nullptr);
120 timeMap_h_hold, lorentzMap_e_hold, lorentzMap_h_hold,
121 eFieldMap_hold,
nullptr));
125 TString prename =
"map_layer_";
127 prename +=
"distance_e.root";
128 distanceMap_e_hold->SaveAs(prename,
"");
129 prename.ReplaceAll(
"_e",
"_h");
130 distanceMap_h_hold->SaveAs(prename,
"");
131 prename.ReplaceAll(
"distance",
"time");
132 timeMap_h_hold->SaveAs(prename,
"");
133 prename.ReplaceAll(
"_h",
"_e");
134 timeMap_e_hold->SaveAs(prename,
"");
135 prename.ReplaceAll(
"time",
"lorentz");
136 lorentzMap_e_hold->SaveAs(prename,
"");
137 prename.ReplaceAll(
"_e",
"_h");
138 lorentzMap_h_hold->SaveAs(prename,
"");
141 if (!distanceMap_e_hold || !distanceMap_h_hold || !timeMap_e_hold || !timeMap_h_hold ||
142 !lorentzMap_e_hold || !lorentzMap_h_hold) {
143 ATH_MSG_ERROR(
"Unable to load at least one of the distance/time/Lorentz angle maps.");
144 return StatusCode::FAILURE;
147 lorentzMap_e_hold->SetDirectory(
nullptr);
148 lorentzMap_h_hold->SetDirectory(
nullptr);
149 distanceMap_e_hold->SetDirectory(
nullptr);
150 distanceMap_h_hold->SetDirectory(
nullptr);
151 timeMap_e_hold->SetDirectory(
nullptr);
152 timeMap_h_hold->SetDirectory(
nullptr);
163 delete eFieldMap_hold;
164 delete lorentzMap_e_hold;
165 delete lorentzMap_h_hold;
166 delete distanceMap_e_hold;
167 delete distanceMap_h_hold;
168 delete timeMap_e_hold;
169 delete timeMap_h_hold;
178 constexpr std::size_t numberOfLayers = 4;
184 return StatusCode::FAILURE;
189 return StatusCode::FAILURE;
194 return StatusCode::FAILURE;
199 return StatusCode::FAILURE;
203 ATH_MSG_INFO(
"Reading histogram: " <<
i <<
" for Lorentz angle correction needed for Pixel radiation damage simulation");
204 std::unique_ptr<TH1>
h(
file->Get<TH1>(
i.c_str()));
206 ATH_MSG_ERROR(
"Cannot read histogram " <<
i <<
" needed for Lorentz angle correction");
207 return StatusCode::FAILURE;
215 ATH_MSG_INFO(
"Reading histogram: " <<
i <<
" for charge correction needed for Pixel radiation damage simulation");
216 std::unique_ptr<TH1>
h(
file->Get<TH1>(
i.c_str()));
218 ATH_MSG_ERROR(
"Cannot read histogram " <<
i <<
" needed for charge correction");
219 return StatusCode::FAILURE;
227 ATH_MSG_INFO(
"Reading histogram: " <<
i <<
" for distance correction needed for Pixel radiation damage simulation");
228 std::unique_ptr<TH1>
h(
file->Get<TH1>(
i.c_str()));
230 ATH_MSG_ERROR(
"Cannot read histogram " <<
i <<
" needed for distance correction");
231 return StatusCode::FAILURE;
241 return StatusCode::SUCCESS;
249 return StatusCode::SUCCESS;
259 std::vector< std::pair<double, double> >& trfHitRecord,
260 std::vector<double>& initialConditions,
261 CLHEP::HepRandomEngine* rndmEngine,
262 const EventContext &ctx)
const {
268 return StatusCode::SUCCESS;
273 if (!Module.
isDBM()) {
274 return StatusCode::SUCCESS;
283 double eta_0 = initialConditions[0];
284 double phi_0 = initialConditions[1];
285 double depth_0 = initialConditions[2];
286 double dEta = initialConditions[3];
287 double dPhi = initialConditions[4];
288 double dDepth = initialConditions[5];
289 double ncharges = initialConditions[6];
290 double iTotalLength = initialConditions[7];
292 const double oneOverNchargesTimes1e6 = 1./(1.E+6 * ncharges);
299 int etaCells = p_design.
columns();
300 int phiCells = p_design.
rows();
302 double eleholePairEnergy = 0;
303 double smearRand = 0;
305 double diffusionConstant;
306 if (Module.
isDBM()) {
307 eleholePairEnergy = 1. / (13. *
CLHEP::eV);
308 diffusionConstant = .00265;
309 smearRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
312 diffusionConstant = .007;
316 double smearScale = 1. + 0.35 * smearRand;
317 double tanLorentz(0);
321 coLorentz = std::sqrt(1.0 + (tanLorentz*tanLorentz));
329 const double pHitTime =
hitTime(phit);
331 const double halfEtaPitch = 0.5*Module.
etaPitch();
332 const double halfPhiPitch = 0.5*Module.
phiPitch();
338 std::pair<double, double> trappingTimes;
352 std::map<unsigned, std::pair<SiLocalPosition, double>> cachedChargeMap;
353 std::map<unsigned, SiCellId> diodeCellMap;
354 for (
const auto& iHitRecord : trfHitRecord) {
356 double eta_i = eta_0;
357 double phi_i = phi_0;
358 double depth_i = depth_0;
360 eta_i += 1.0 * iHitRecord.first / iTotalLength *
dEta;
361 phi_i += 1.0 * iHitRecord.first / iTotalLength *
dPhi;
362 depth_i += 1.0 * iHitRecord.first / iTotalLength * dDepth;
368 if (!pixel_i.
isValid())
continue;
372 double dist_electrode = 0.5 * sensorThickness - Module.
design().
readoutSide() * depth_i;
373 if (dist_electrode < 0) {
381 const int nnLoop_pixelEtaMin =
std::max(-1, pixel_i.
etaIndex() + 1 - etaCells);
383 const int nnLoop_pixelPhiMin =
std::max(-1, pixel_i.
phiIndex() + 1 - phiCells);
385 std::array<double, 3> sensorScales{};
387 const std::size_t distance_f_e_bin_x = distanceMap_e.
getBinX(dist_electrode);
388 const std::size_t distance_f_h_bin_x = distanceMap_h.
getBinX(dist_electrode);
389 const std::size_t tanLorentz_e_bin_x = lorentzMap_e.
getBinX(dist_electrode);
390 const std::size_t tanLorentz_h_bin_x = lorentzMap_h.
getBinX(dist_electrode);
392 const std::size_t sizePhi = std::abs(nnLoop_pixelPhiMax - nnLoop_pixelPhiMin) + 1;
394 const auto pixel_eta = pixel_i.
etaIndex();
395 const auto pixel_phi = pixel_i.
phiIndex();
398 std::array<std::pair<double,double>,9 > centrePixelNNEtaPhi;
399 for (
int p = nnLoop_pixelEtaMin;
p <= nnLoop_pixelEtaMax;
p++) {
400 const std::size_t ieta =
p - nnLoop_pixelEtaMin;
404 if (std::abs(columnWidth - 0.6) < 1
e-9) {
406 }
else if (std::abs(columnWidth - 0.45) < 1
e-9) {
408 }
else if (std::abs(columnWidth - 0.5) < 1
e-9) {
411 sensorScales[ieta] = scale_f;
413 for (
int q = nnLoop_pixelPhiMin;
q <= nnLoop_pixelPhiMax;
q++) {
416 const std::size_t iphi =
q - nnLoop_pixelPhiMin;
417 const std::size_t
index = iphi + ieta*sizePhi;
418 centrePixelNNEtaPhi.at(
index).first = centreOfPixel_nn.
xEta();
419 centrePixelNNEtaPhi[
index].second = centreOfPixel_nn.
xPhi();
424 for (
int j = 0; j < ncharges; j++) {
426 double energy_per_step = oneOverNchargesTimes1e6 * iHitRecord.second;
427 double u = CLHEP::RandFlat::shoot(rndmEngine, 0.0, 1.0);
429 const double drifttime_e = (-1.) * (trappingTimes.first) * logf(
u);
430 u = CLHEP::RandFlat::shoot(rndmEngine, 0.0, 1.0);
431 const double drifttime_h = (-1.) * (trappingTimes.second) * logf(
u);
439 const double depth_f_e = distanceMap_e.
getContent(distance_f_e_bin_x, distanceMap_e.
getBinY(drifttime_e));
440 const double depth_f_h = distanceMap_h.
getContent(distance_f_h_bin_x, distanceMap_h.
getBinY(drifttime_h));
441 const double tanLorentz_e = lorentzMap_e.
getContent(tanLorentz_e_bin_x, lorentzMap_e.
getBinY(depth_f_e));
442 const double tanLorentz_h = lorentzMap_h.
getContent(tanLorentz_h_bin_x, lorentzMap_h.
getBinY(depth_f_h));
443 const double dz_e = std::abs(dist_electrode - depth_f_e);
444 const double dz_h = std::abs(depth_f_h - dist_electrode);
445 const double coLorentz_e = std::sqrt(1.0 + (tanLorentz_e*tanLorentz_e));
448 double phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
451 const double rdif_e = diffusionConstant * std::sqrt(dz_e * coLorentz_e / 0.3);
452 const double phi_f_e = phi_i + dz_e * tanLorentz_e + rdif_e * phiRand;
453 double etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
454 double eta_f_e = eta_i + rdif_e * etaRand;
456 phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
457 const double coLorentz_h = std::sqrt(1.0 + (tanLorentz_h*tanLorentz_h));
458 const double rdif_h = diffusionConstant * std::sqrt(dz_h * coLorentz_h / 0.3);
459 const double phi_f_h = phi_i + dz_h * tanLorentz_h + rdif_h * phiRand;
460 etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
461 double eta_f_h = eta_i + rdif_h * etaRand;
470 const std::size_t ramo_f_e_bin_z = ramoPotentialMap.
getBinZ(1
e3*depth_f_e);
471 const std::size_t ramo_f_h_bin_z = ramoPotentialMap.
getBinZ(1
e3*depth_f_h);
473 const bool isFirstZ_e = ramoPotentialMap.
isFirstZ(1
e3*depth_f_e);
474 const bool isOverflowZ_h = ramoPotentialMap.
isOverflowZ(1
e3*depth_f_h);
476 const double pixelEta_f_e = eta_f_e - centreOfPixel_i.
xEta();
477 const double pixelPhi_f_e = phi_f_e - centreOfPixel_i.
xPhi();
479 const double pixelEta_f_h = eta_f_h - centreOfPixel_i.
xEta();
480 const double pixelPhi_f_h = phi_f_h - centreOfPixel_i.
xPhi();
484 for (
int p = nnLoop_pixelEtaMin;
p <= nnLoop_pixelEtaMax;
p++) {
485 const std::size_t ieta =
p - nnLoop_pixelEtaMin;
487 for (
int q = nnLoop_pixelPhiMin;
q <= nnLoop_pixelPhiMax;
q++) {
491 const std::size_t iphi =
q - nnLoop_pixelPhiMin;
492 const std::size_t
index = iphi + ieta*sizePhi;
495 const std::pair<double,double>& centrePixelNN = centrePixelNNEtaPhi.at(
index);
496 const double dPhi_nn_centre = centrePixelNN.second - centreOfPixel_i.
xPhi();
497 const double dEta_nn_centre = centrePixelNN.first - centreOfPixel_i.
xEta();
505 const double dEta_f_e = std::abs(pixelEta_f_e - dEta_nn_centre)*sensorScales[ieta];
506 const double dPhi_f_e = std::abs(pixelPhi_f_e - dPhi_nn_centre);
507 const double dEta_f_h = 1
e3*std::abs(pixelEta_f_h - dEta_nn_centre)*sensorScales[ieta];
508 const double dPhi_f_h = 1
e3*std::abs(pixelPhi_f_h - dPhi_nn_centre);
511 double ramo_f_e = 0.0;
512 double ramo_f_h = 0.0;
515 if (dEta_f_e >= halfEtaPitch || dPhi_f_e >= halfPhiPitch) {
528 ramo_f_h = ramoPotentialMap.
getContent(ramoPotentialMap.
getBinX(dPhi_f_h), ramoPotentialMap.
getBinY(dEta_f_h), ramo_f_h_bin_z);
534 const double potentialDiff = ramo_f_e - ramo_f_h;
536 const double induced_charge = potentialDiff * energy_per_step * eleholePairEnergy;
538 unsigned key = (
static_cast<unsigned>(pixel_eta-
p) << 16) |
static_cast<unsigned>(pixel_phi-
q);
539 auto cacheIterator = cachedChargeMap.find(
key);
540 if(cacheIterator == cachedChargeMap.end()) {
541 cachedChargeMap.insert(std::make_pair(
key, std::make_pair(Module.
hitLocalToLocal(centrePixelNN.first, centrePixelNN.second), induced_charge)));
543 cacheIterator->second.second += induced_charge;
550 std::for_each(cachedChargeMap.begin(), cachedChargeMap.end(), [&diodeCellMap, &Module, &chargedDiodes, &pHitTime, &particleLink](
auto& pos_charge_pair){
551 auto& key = pos_charge_pair.first;
552 auto& chargePos = pos_charge_pair.second.first;
553 auto& charge_value = pos_charge_pair.second.second;
555 const SiSurfaceCharge scharge(chargePos, SiCharge(charge_value, pHitTime, SiCharge::track, particleLink));
556 auto diodeIterator = diodeCellMap.find(key);
557 if(diodeIterator == diodeCellMap.end()) diodeIterator = diodeCellMap.insert(std::make_pair(key, Module.cellIdOfPosition(scharge.position()))).first;
558 const SiCellId& thisDiode = diodeIterator->second;
559 if (thisDiode.isValid()) {
560 const SiCharge& charge = scharge.charge();
561 chargedDiodes.add(thisDiode, charge);
566 else if (m_radiationDamageSimulationType == RadiationDamageSimulationType::TEMPLATE_CORRECTION && !(Module.isDBM()) && Module.isBarrel()){
572 int layerToRead = isITk && m_digitizeITk3Das3D ?
layer - 1 :
layer;
575 if (layerToRead > 3) {
583 for (
const auto & iHitRecord : trfHitRecord) {
584 double eta_i = eta_0;
585 double phi_i = phi_0;
586 double depth_i = depth_0;
588 eta_i += 1.0 * iHitRecord.first / iTotalLength *
dEta;
589 phi_i += 1.0 * iHitRecord.first / iTotalLength *
dPhi;
590 depth_i += 1.0 * iHitRecord.first / iTotalLength * dDepth;
598 tanLorentz = lorentzCorrectionHist.
getContent(depthZ);
599 coLorentz = std::sqrt(1.0 + (tanLorentz*tanLorentz));
602 const double chargeCorrection = chargeCorrectionHist.
getContent(depthZ);
605 double nontrappingProbability = 1.0;
606 if (Module.isDBM()) {
607 nontrappingProbability =
exp(-dist_electrode / collectionDist);
610 for (
int j = 0; j < ncharges; j++) {
612 double energy_per_step = oneOverNchargesTimes1e6 * iHitRecord.second;
614 double rdif = diffusionConstant * std::sqrt(dist_electrode * coLorentz / 0.3);
617 double phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
618 double phi_drifted = phi_i + dist_electrode * tanLorentz + rdif * phiRand;
619 double etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
620 double eta_drifted = eta_i + rdif * etaRand;
624 applyIBLSlimEdges(energy_per_step, eta_drifted);
628 const SiLocalPosition& chargePos = Module.hitLocalToLocal(eta_drifted, phi_drifted);
632 if (Module.isDBM()) {
633 ed = energy_per_step * eleholePairEnergy * nontrappingProbability * smearScale;
635 ed = energy_per_step * eleholePairEnergy;
638 ed *= chargeCorrection;
643 const SiCellId& diode = Module.cellIdOfPosition(scharge.position());
647 chargedDiodes.add(diode,
charge);
652 for (
const auto & iHitRecord : trfHitRecord) {
653 double eta_i = eta_0;
654 double phi_i = phi_0;
655 double depth_i = depth_0;
657 eta_i += 1.0 * iHitRecord.first / iTotalLength *
dEta;
658 phi_i += 1.0 * iHitRecord.first / iTotalLength *
dPhi;
659 depth_i += 1.0 * iHitRecord.first / iTotalLength * dDepth;
664 double dist_electrode = 0.5 * sensorThickness - Module.design().readoutSide() * depth_i;
665 if (dist_electrode < 0) {
670 double nontrappingProbability = 1.0;
671 if (Module.isDBM()) {
672 nontrappingProbability =
exp(-dist_electrode / collectionDist);
675 for (
int j = 0; j < ncharges; j++) {
677 double energy_per_step = oneOverNchargesTimes1e6 * iHitRecord.second;
679 double rdif = diffusionConstant * std::sqrt(dist_electrode * coLorentz / 0.3);
682 double phiRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
683 double phi_drifted = phi_i + dist_electrode * tanLorentz + rdif * phiRand;
684 double etaRand = CLHEP::RandGaussZiggurat::shoot(rndmEngine);
685 double eta_drifted = eta_i + rdif * etaRand;
689 applyIBLSlimEdges(energy_per_step, eta_drifted);
693 const SiLocalPosition& chargePos = Module.hitLocalToLocal(eta_drifted, phi_drifted);
697 if (Module.isDBM()) {
698 ed = energy_per_step * eleholePairEnergy * nontrappingProbability * smearScale;
700 ed = energy_per_step * eleholePairEnergy;
706 const SiCellId& diode = Module.cellIdOfPosition(scharge.position());
710 chargedDiodes.add(diode,
charge);
716 return StatusCode::SUCCESS;
720 if (std::abs(eta_drifted) > 20.440) {
721 energy_per_step = 0.0;
723 if (std::abs(eta_drifted) < 20.440 && std::abs(eta_drifted) > 20.200) {
724 if (eta_drifted > 0) {
725 energy_per_step = energy_per_step * (68.13 - eta_drifted * 3.333);
726 eta_drifted = eta_drifted - 0.250;
728 energy_per_step = energy_per_step * (68.13 + eta_drifted * 3.333);
729 eta_drifted = eta_drifted + 0.250;
732 if (std::abs(eta_drifted) < 20.200 && std::abs(eta_drifted) > 20.100) {
733 if (eta_drifted > 0) {
734 energy_per_step = energy_per_step * (41.2 - eta_drifted * 2.0);
735 eta_drifted = eta_drifted - 0.250;
737 energy_per_step = energy_per_step * (41.2 + eta_drifted * 2.0);
738 eta_drifted = eta_drifted + 0.250;