ATLAS Offline Software
Loading...
Searching...
No Matches
FEI3SimTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4#include "FEI3SimTool.h"
7#include "PixelConditionsData/ChargeCalibParameters.h" //for Thresholds
13//
14#include "CLHEP/Random/RandGaussZiggurat.h"
15#include "CLHEP/Random/RandFlat.h"
16
17#include <cmath>
18#include "PixelNoiseFunctions.h"
19
20using namespace PixelDigitization;
21
22
23FEI3SimTool::FEI3SimTool(const std::string& type, const std::string& name, const IInterface* parent) :
24 FrontEndSimTool(type, name, parent) {
25}
26
29 ATH_MSG_DEBUG("FEI3SimTool::initialize()");
30 ATH_CHECK(m_moduleDataKey.initialize());
31 return StatusCode::SUCCESS;
32}
33
34void FEI3SimTool::process(const EventContext& ctx,
35 SiChargedDiodeCollection& chargedDiodes, PixelRDO_Collection& rdoCollection,
36 CLHEP::HepRandomEngine* rndmEngine) const {
37 const InDetDD::PixelModuleDesign* p_design =
38 static_cast<const InDetDD::PixelModuleDesign*>(&(chargedDiodes.element())->design());
39
41 return;
42 }
43
44 const PixelID* pixelId = static_cast<const PixelID*>(chargedDiodes.element()->getIdHelper());
45 const IdentifierHash moduleHash = pixelId->wafer_hash(chargedDiodes.identify()); // wafer hash
46 Identifier moduleID = pixelId->wafer_id(chargedDiodes.element()->identify());
47
48 int barrel_ec = pixelId->barrel_ec(chargedDiodes.element()->identify());
49 int layerIndex = pixelId->layer_disk(chargedDiodes.element()->identify());
50 int moduleIndex = pixelId->eta_module(chargedDiodes.element()->identify());
51
52 if (std::abs(barrel_ec) != m_BarrelEC) {
53 return;
54 }
55
57 const PixelModuleData *moduleData = *moduleDataHandle;
59 const PixelChargeCalibCondData *calibData = *calibDataHandle;
60 const auto selectedTuneYear = moduleData->getFEI3TimingSimTune(barrel_ec, layerIndex);
61 // Add cross-talk
62 crossTalk(moduleData->getCrossTalk(barrel_ec, layerIndex), chargedDiodes);
63
64 if (m_doNoise) {
65 // Add thermal noise
66 thermalNoise(m_thermalNoise, chargedDiodes, rndmEngine);
67
68 // Add random noise
69 randomNoise(chargedDiodes, moduleData, m_numberOfBcid, calibData, rndmEngine, m_pixelReadout.get());
70 }
71
72 // Add random diabled pixels
73 randomDisable(chargedDiodes, moduleData, rndmEngine); // FIXME How should we handle disabling pixels in Overlay jobs?
74 const InDetDD::SiDetectorElement * siDetEl = static_cast<const InDetDD::SiDetectorElement *>(chargedDiodes.element());
75 for (auto &[mapId,mapDiode]:chargedDiodes) {
76 // Merge ganged pixel
77 InDetDD::SiCellId cellID = chargedDiodes.element()->cellIdFromIdentifier(chargedDiodes.getId( mapId));
78 InDetDD::SiCellId gangedCell = siDetEl->gangedCell(cellID);
79 Identifier gangedID = chargedDiodes.element()->identifierFromCellId(gangedCell);
80 if (gangedCell.isValid()) {
81 SiChargedDiode* gangedChargeDiode = chargedDiodes.find(gangedID);
82 int phiGanged = pixelId->phi_index(gangedID);
83 int phiThis = pixelId->phi_index(chargedDiodes.getId( mapId));
84
85 if (gangedChargeDiode) { // merge charges
86 bool maskGanged = ((phiGanged > 159) && (phiGanged < 168));
87 bool maskThis = ((phiThis > 159) && (phiThis < 168));
88 // mask the one ganged pixel that does not correspond to the readout electronics.
89 // not really sure this is needed
90 if (maskGanged && maskThis) {
91 ATH_MSG_ERROR("FEI3SimTool: both ganged pixels are in the mask out region -> BUG!");
92 }
93 if (maskGanged) {
94 mapDiode.add(gangedChargeDiode->totalCharge()); // merged org pixel
95 SiHelper::maskOut(*gangedChargeDiode, true);
96 } else {
97 gangedChargeDiode->add(mapDiode.totalCharge()); // merged org pixel
98 SiHelper::maskOut(mapDiode, true);
99 }
100 }
101 }
102 }
103
104 for (SiChargedDiodeOrderedIterator i_chargedDiode = chargedDiodes.orderedBegin();
105 i_chargedDiode != chargedDiodes.orderedEnd(); ++i_chargedDiode) {
106 SiChargedDiode& diode = **i_chargedDiode;
107
108 Identifier diodeID = chargedDiodes.getId(diode.diode());
109 double charge = diode.charge();
110
111 unsigned int FE = m_pixelReadout->getFE(diodeID, moduleID);
112 InDetDD::PixelDiodeType type = m_pixelReadout->getDiodeType(diodeID);
113 if ((FE == InDetDD::invalidFrontEnd) or (type == InDetDD::PixelDiodeType::NONE)) continue;//invalid frontend
114
115 // charge to ToT conversion
116 double tot = calibData->getToT(type, moduleHash, FE, charge);
117 const auto thresholds = calibData->getThresholds(type, moduleHash, FE);
118 // Apply analog threshold, timing simulation
119 double th0 = thresholds.value;
120 double ith0 = thresholds.inTimeValue;
121 double threshold = PixelDigitization::randomThreshold(thresholds, rndmEngine);
122 // This noise check is unaffected by digitizationFlags.doInDetNoise in
123 // 21.0 - see PixelCellDiscriminator.cxx in that branch
124
125 if (charge > threshold) {
126 int bunchSim = 0;
127 if (diode.totalCharge().fromTrack()) {
128 const std::vector<float> & totCharges = moduleData->getTimingIndex(barrel_ec, layerIndex);
129 const std::vector<float> & probArray = moduleData->getTimingProbability(barrel_ec, layerIndex, moduleIndex);
130
131 double prob = 0.0;
132 if (selectedTuneYear==2023) { prob = getProbability(totCharges, probArray, tot); }
133 if (selectedTuneYear==2022) { prob = getProbability(totCharges, probArray, tot); }
134 if (selectedTuneYear==2018) { prob = getProbability(totCharges, probArray, diode.totalCharge().charge()); }
135 if (selectedTuneYear==2015) { prob = getProbability(totCharges, probArray, diode.totalCharge().charge()); }
136
137 double G4Time = getG4Time(diode.totalCharge());
138 double rnd = CLHEP::RandFlat::shoot(rndmEngine, 0.0, 1.0);
139
140 double timeWalk = 0.0;
141 if (rnd<prob) { timeWalk = 25.0; }
142 bunchSim = static_cast<int>(std::floor((G4Time+m_timeOffset+timeWalk)/m_bunchSpace));
143
144 if (selectedTuneYear == 2009) { // RUN1 procedure (based on 2007 cosmic data)
145 double intimethreshold = (ith0 / th0) * threshold;
146 bunchSim = relativeBunch2009(threshold, intimethreshold, diode.totalCharge(), rndmEngine);
147 }
148 }
149 else {
150 if (moduleData->getFEI3TimingSimTune(barrel_ec, layerIndex) > 0) {
151 bunchSim = CLHEP::RandFlat::shootInt(rndmEngine, m_numberOfBcid);
152 }
153 }
154
155 if (bunchSim < 0 || bunchSim > m_numberOfBcid) {
156 SiHelper::belowThreshold(diode, true, true);
157 } else {
158 SiHelper::SetBunch(diode, bunchSim);
159 }
160 } else {
161 SiHelper::belowThreshold(diode, true, true);
162 }
163
164 double totsig = calibData->getTotRes(moduleHash, FE, tot);
165 int nToT = static_cast<int>(CLHEP::RandGaussZiggurat::shoot(rndmEngine, tot, totsig));
166
167 if (nToT < 1) {
168 nToT = 1;
169 }
170
171 if (nToT <= moduleData->getToTThreshold(barrel_ec, layerIndex)) {
172 SiHelper::belowThreshold(diode, true, true);
173 }
174
175 if (nToT >= moduleData->getFEI3Latency(barrel_ec, layerIndex)) {
176 SiHelper::belowThreshold(diode, true, true);
177 }
178
179 // Filter events
180 if (SiHelper::isMaskOut(diode)) {
181 continue;
182 }
183 if (SiHelper::isDisabled(diode)) {
184 continue;
185 }
186
187 if (!m_pixelConditionsTool->isActive(moduleHash, diodeID, ctx)) {
188 SiHelper::disabled(diode, true, true);
189 continue;
190 }
191
192 int flag = diode.flag();
193 int bunch = (flag >> 8) & 0xff;
194
196 const Identifier id_readout = chargedDiodes.element()->identifierFromCellId(cellId);
197
198 // Front-End simulation
199 if (bunch >= 0 && bunch < m_numberOfBcid) {
200 rdoCollection.push_back(new Pixel1RawData(id_readout, nToT, bunch, 0, bunch));
201 }
202
203 // Duplication mechanism for FEI3 small hits :
204 if (m_duplication) {//is true for run1 only
205 static constexpr int smallHitThreshold{7}; //constant for both barrel and endcap, never changes
206 bool smallHitChk = false;
207 if (nToT <= smallHitThreshold) {
208 smallHitChk = true;
209 }
210 if (smallHitChk && bunch > 0 && bunch <= m_numberOfBcid) {
211 rdoCollection.push_back(new Pixel1RawData(id_readout, nToT, bunch - 1, 0, bunch - 1));
212 }
213 }
214 }
215 }
216
217int FEI3SimTool::relativeBunch2009(const double threshold, const double intimethreshold,
218 const SiTotalCharge& totalCharge,
219 CLHEP::HepRandomEngine* rndmEngine) const {
220 int BCID = 0;
221 double myTimeWalkEff = 0.;
222 double overdrive = intimethreshold - threshold;
223
224 //my TimeWalk computation through PARAMETRIZATION (by Francesco De Lorenzi - Milan)
225 //double curvature = 7.6e7*overdrive-2.64e10;
226 //double divergence = -1.6*overdrive+942 ;
227 //double myTimeWalk = curvature/(pow((totalCharge.charge()-divergence),2.5));
228
229 //my TimeWalk computation through PARAMETRIZATION from 2009 cosmic data (by I. Ibragimov and D. Miller)
230 double p1 = 20. / std::log(intimethreshold / overdrive);
231 double p0 = p1 * std::log(1. - threshold / 100000.);
232
233 double myTimeWalk = -p0 - p1 * std::log(1. - threshold / totalCharge.charge());
234
235 myTimeWalkEff = myTimeWalk + myTimeWalk * 0.2 * CLHEP::RandGaussZiggurat::shoot(rndmEngine);
236 const double limit = m_timeJitter * 0.5;
237 double randomJitter = CLHEP::RandFlat::shoot(rndmEngine, - limit, limit);
238
239 //double G4Time = totalCharge.time();
240
241 double G4Time = getG4Time(totalCharge);
242 double timing = m_timeOffset + myTimeWalkEff + randomJitter + G4Time;
243 BCID = static_cast<int>(std::floor(timing / m_bunchSpace));
244 //ATH_MSG_DEBUG ( CTW << " , " << myTimeWalkEff << " , " << G4Time << " , " << timing << " , " << BCID );
245
246 return BCID;
247}
248
249double FEI3SimTool::getProbability(const std::vector<float> &bounds, const std::vector<float> &probs, const double &val) const {
250 auto pCategory = std::upper_bound(bounds.begin(), bounds.end(),val);
251 if (pCategory == bounds.end()) return 0.0;
252 auto idx = std::distance(bounds.begin(), pCategory);
253 return probs[idx];
254}
255
256
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_DEBUG(x)
double charge(const T &p)
Definition AtlasPID.h:997
Structs for holding charge calibration parameterisation and data.
static TRandom * rnd
This is an Identifier helper class for the Pixel subdetector.
InDetRawDataCollection< PixelRDORawData > PixelRDO_Collection
SiChargedDiodeOrderedSet::iterator SiChargedDiodeOrderedIterator
value_type push_back(value_type pElem)
virtual StatusCode initialize() override
int relativeBunch2009(const double threshold, const double intimethreshold, const SiTotalCharge &totalCharge, CLHEP::HepRandomEngine *rndmEngine) const
FEI3SimTool(const std::string &type, const std::string &name, const IInterface *parent)
virtual void process(const EventContext &ctx, SiChargedDiodeCollection &chargedDiodes, PixelRDO_Collection &rdoCollection, CLHEP::HepRandomEngine *rndmEngine) const override
double getProbability(const std::vector< float > &bounds, const std::vector< float > &probs, const double &val) const
Gaudi::Property< bool > m_duplication
Definition FEI3SimTool.h:40
SG::ReadCondHandleKey< PixelModuleData > m_moduleDataKey
Definition FEI3SimTool.h:35
SG::ReadCondHandleKey< PixelChargeCalibCondData > m_chargeDataKey
ServiceHandle< InDetDD::IPixelReadoutManager > m_pixelReadout
virtual StatusCode initialize() override
static constexpr double m_bunchSpace
ToolHandle< IInDetConditionsTool > m_pixelConditionsTool
Gaudi::Property< bool > m_doNoise
Gaudi::Property< int > m_BarrelEC
This is a "hash" representation of an Identifier.
Class used to describe the design of a module (diode segmentation and readout scheme).
PixelReadoutTechnology getReadoutTechnology() const
Identifier for the strip or pixel cell.
Definition SiCellId.h:29
bool isValid() const
Test if its in a valid state.
Definition SiCellId.h:136
Class to hold geometrical description of a silicon detector element.
SiCellId gangedCell(const SiCellId &cellId) const
If cell is ganged return the id of the other cell which shares the readout for this cell,...
Identifier for the strip or pixel readout cell.
virtual SiCellId cellIdFromIdentifier(const Identifier &identifier) const =0
SiCellId from Identifier.
virtual Identifier identify() const override final
identifier of this detector element (inline)
virtual Identifier identifierFromCellId(const SiCellId &cellId) const =0
Identifier <-> SiCellId (ie strip number or pixel eta_index,phi_index) Identifier from SiCellId (ie s...
PixelChargeCalib::Thresholds getThresholds(InDetDD::PixelDiodeType type, unsigned int moduleHash, unsigned int FE) const
float getToT(InDetDD::PixelDiodeType type, unsigned int moduleHash, unsigned int FE, float Q) const
float getTotRes(unsigned int moduleHash, unsigned int FE, float Q) const
This is an Identifier helper class for the Pixel subdetector.
Definition PixelID.h:69
int layer_disk(const Identifier &id) const
Definition PixelID.h:602
Identifier wafer_id(int barrel_ec, int layer_disk, int phi_module, int eta_module) const
For a single crystal.
Definition PixelID.h:355
int barrel_ec(const Identifier &id) const
Values of different levels (failure returns 0).
Definition PixelID.h:595
IdentifierHash wafer_hash(Identifier wafer_id) const
wafer hash from id
Definition PixelID.h:378
int eta_module(const Identifier &id) const
Definition PixelID.h:627
int phi_index(const Identifier &id) const
Definition PixelID.h:634
int getFEI3Latency(int barrel_ec, int layer) const
int getFEI3TimingSimTune(int barrel_ec, int layer) const
std::vector< float > getTimingProbability(int barrel_ec, int layer, int eta) const
std::vector< float > getTimingIndex(int barrel_ec, int layer) const
double getCrossTalk(int barrel_ec, int layer) const
virtual Identifier identify() const override final
SiChargedDiodeOrderedIterator orderedEnd()
SiChargedDiodeOrderedIterator orderedBegin()
SiChargedDiode * find(const InDetDD::SiCellId &siId)
Identifier getId(const InDetDD::SiCellId &id) const
const InDetDD::SolidStateDetectorElementBase * element() const
const InDetDD::SiCellId & diode() const
int flag() const
double charge() const
const SiTotalCharge & totalCharge() const
void add(const SiCharge &charge)
const InDetDD::SiReadoutCellId & getReadoutCell() const
static bool isMaskOut(SiChargedDiode &chDiode)
Definition SiHelper.h:171
static void SetBunch(SiChargedDiode &chDiode, int bunch, MsgStream *log=nullptr)
Definition SiHelper.h:129
static void disabled(SiChargedDiode &chDiode, bool flag, bool mask=false)
Definition SiHelper.h:93
static void maskOut(SiChargedDiode &chDiode, bool flag)
Definition SiHelper.h:67
static void belowThreshold(SiChargedDiode &chDiode, bool flag, bool mask=false)
Definition SiHelper.h:84
static bool isDisabled(SiChargedDiode &chDiode)
Definition SiHelper.h:179
bool fromTrack() const
double charge() const
constexpr uint32_t invalidFrontEnd
void randomDisable(SiChargedDiodeCollection &chargedDiodes, const PixelModuleData *moduleData, CLHEP::HepRandomEngine *rndmEngine)
void crossTalk(double crossTalk, SiChargedDiodeCollection &chargedDiodes)
void thermalNoise(double thermalNoise, SiChargedDiodeCollection &chargedDiodes, CLHEP::HepRandomEngine *rndmEngine)
double randomThreshold(const PixelChargeCalib::Thresholds &t, CLHEP::HepRandomEngine *pEngine)
void randomNoise(SiChargedDiodeCollection &chargedDiodes, const PixelModuleData *moduleData, int nBcid, const PixelChargeCalibCondData *chargeCalibData, CLHEP::HepRandomEngine *rndmEngine, InDetDD::IPixelReadoutManager *pixelReadout)
double getG4Time(const SiTotalCharge &totalCharge)