ATLAS Offline Software
Loading...
Searching...
No Matches
sTgcFastDigiTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4#include "sTgcFastDigiTool.h"
6#include "CLHEP/Random/RandGaussZiggurat.h"
7#include "CLHEP/Random/RandFlat.h"
9namespace {
10 constexpr double percentage(unsigned int numerator, unsigned int denom) {
11 return 100. * numerator / std::max(denom, 1u);
12 }
13 using channelType = sTgcIdHelper::sTgcChannelTypes;
14
15 static const SG::Decorator<uint16_t> dec_sdoPadChannel{"sTgc_padChannel"};
16 static const SG::Decorator<uint16_t> dec_sdoWireChannel{"sTgc_wireChannel"};
17
18}
19namespace MuonR4 {
20
23 ATH_CHECK(m_writeKey.initialize());
24 ATH_CHECK(m_effiDataKey.initialize(!m_effiDataKey.empty()));
25 ATH_CHECK(m_uncertCalibKey.initialize());
26 return StatusCode::SUCCESS;
27 }
29 ATH_MSG_INFO("Tried to convert "<<m_allHits[channelType::Strip]<<"/"
30 <<m_allHits[channelType::Wire]<<"/"
31 <<m_allHits[channelType::Pad]<<" strip/wire/pad hits. In, "
32 <<percentage(m_acceptedHits[channelType::Strip], m_allHits[channelType::Strip]) <<"/"
33 <<percentage(m_acceptedHits[channelType::Wire], m_allHits[channelType::Wire])<<"/"
34 <<percentage(m_acceptedHits[channelType::Pad], m_allHits[channelType::Pad])
35 <<"% of the cases, the conversion was successful");
36 return StatusCode::SUCCESS;
37 }
38 StatusCode sTgcFastDigiTool::digitize(const EventContext& ctx,
39 const TimedHits& hitsToDigit,
40 xAOD::MuonSimHitContainer* sdoContainer) const {
41 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
42 // Prepare the temporary cache
43 DigiCache digitCache{};
45 const Muon::DigitEffiData* efficiencyMap{nullptr};
46 ATH_CHECK(SG::get(efficiencyMap, m_effiDataKey, ctx));
47 const NswErrorCalibData* nswUncertDB{nullptr};
48 ATH_CHECK(SG::get(nswUncertDB, m_uncertCalibKey, ctx));
49
50 CLHEP::HepRandomEngine* rndEngine = getRandomEngine(ctx);
51 xAOD::ChamberViewer viewer{hitsToDigit, m_idHelperSvc.get()};
52 do {
53 for (const TimedHit& simHit : viewer) {
55 if (m_digitizeMuonOnly && !MC::isMuon(simHit)){
56 continue;
57 }
58
59 if (simHit->energyDeposit() < m_energyDepositThreshold){
60 ATH_MSG_VERBOSE("Hit with Energy Deposit of " << simHit->energyDeposit()
61 << " less than " << m_energyDepositThreshold << ". Skip this hit." );
62 continue;
63 }
64
65 const double hitKineticEnergy = simHit->kineticEnergy();
66 if (hitKineticEnergy < m_limitElectronKineticEnergy && MC::isElectron(simHit)) {
67 ATH_MSG_DEBUG("Skip electron hit with kinetic energy " << hitKineticEnergy
68 << ", which is less than the lower limit of " << m_limitElectronKineticEnergy);
69 continue;
70 }
71 sTgcDigitCollection* digiColl = fetchCollection(simHit->identify(), digitCache);
72 const bool digitizedPad = digitizePad(ctx, simHit, efficiencyMap, rndEngine, *digiColl);
73 const int16_t padChannel = digitizedPad ? idHelper.channel(digiColl->back()->identify()) : -1;
74 const bool digitizedWire = digitizeWire(ctx, simHit, efficiencyMap, rndEngine, *digiColl);
75 const int16_t wireChannel = digitizedWire ? idHelper.channel(digiColl->back()->identify()) : -1;
76 const bool digitizedStrip = digitizeStrip(ctx, simHit, nswUncertDB, efficiencyMap, rndEngine, *digiColl);
77
78 if (digitizedStrip || digitizedPad || digitizedWire) {
79 xAOD::MuonSimHit* sdo = addSDO(simHit, sdoContainer);
80 dec_sdoPadChannel(*sdo) = padChannel;
81 dec_sdoWireChannel(*sdo) = wireChannel;
82 sdo->setIdentifier(digiColl->back()->identify());
83 }
84 }
85 } while (viewer.next());
87 ATH_CHECK(writeDigitContainer(ctx, m_writeKey, std::move(digitCache),
88 idHelper.module_hash_max()));
89 return StatusCode::SUCCESS;
90 }
91 bool sTgcFastDigiTool::digitizeStrip(const EventContext& ctx,
92 const TimedHit& timedHit,
93 const NswErrorCalibData* errorCalibDB,
94 const Muon::DigitEffiData* efficiencyMap,
95 CLHEP::HepRandomEngine* rndEngine,
96 sTgcDigitCollection& outCollection) const {
97
98 if (!m_digitizeStrip) {
99 return false;
100 }
101 ++m_allHits[channelType::Strip];
102 const Identifier hitId{timedHit->identify()};
103 const MuonGMR4::sTgcReadoutElement* readOutEle{m_detMgr->getsTgcReadoutElement(hitId)};
104
105 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
106
107 const IdentifierHash meashHash = readOutEle->measurementHash(hitId);
108 const MuonGMR4::StripDesign& design{readOutEle->stripDesign(meashHash)};
109
110 const Amg::Vector2D stripPos = readOutEle->stripLayer(meashHash)
111 .to2D(xAOD::toEigen(timedHit->localPosition()), false);
112
113 const int stripNum = design.stripNumber(stripPos);
114 if (stripNum < 0) {
115 ATH_MSG_VERBOSE("Strip hit "<<Amg::toString(stripPos)<<" "<<m_idHelperSvc->toStringGasGap(hitId)
116 <<" is out of range "<<std::endl<<design);
117 return false;
118 }
119
120
121 bool isValid{false};
122 const int gasGap = idHelper.gasGap(hitId);
123 const Identifier stripId = idHelper.channelID(hitId, readOutEle->multilayer(),
124 gasGap, channelType::Strip, stripNum, isValid);
125
126 if (!isValid) {
127 ATH_MSG_WARNING("Failed to deduce a valid identifier from "
128 <<m_idHelperSvc->toStringGasGap(hitId)<<" strip: "<<stripNum);
129 return false;
130 }
131
133 bool isInnerQ1 = readOutEle->isEtaZero(readOutEle->measurementHash(hitId), stripPos);
134 if (efficiencyMap && efficiencyMap->getEfficiency(hitId, isInnerQ1) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
135 ATH_MSG_VERBOSE("Simulated strip hit "<<xAOD::toEigen(timedHit->localPosition())
136 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
137 return false;
138 }
139
140 NswErrorCalibData::Input errorCalibInput{};
141 errorCalibInput.stripId= stripId;
142 errorCalibInput.locTheta = M_PI - timedHit->localDirection().theta();
143 errorCalibInput.clusterAuthor = 3; // centroid
144
145 const double uncert = errorCalibDB->clusterUncertainty(errorCalibInput);
146 const double smearedX = CLHEP::RandGaussZiggurat::shoot(rndEngine, stripPos.x(), uncert);
147
148 const Amg::Vector2D digitPos{smearedX * Amg::Vector2D::UnitX()};
149
150 const int digitStrip = design.stripNumber(digitPos);
151 if (digitStrip < 0) {
152 ATH_MSG_VERBOSE("Smeared strip hit "<<Amg::toString(digitPos)<<" "<<m_idHelperSvc->toStringGasGap(hitId)
153 <<" is out of range "<<std::endl<<design);
154 return false;
155 }
156 const Identifier digitId = idHelper.channelID(hitId, readOutEle->multilayer(),
157 gasGap, channelType::Strip, digitStrip, isValid);
158
159 if (!isValid) {
160 ATH_MSG_WARNING("Failed to deduce a valid identifier from "
161 <<m_idHelperSvc->toStringGasGap(hitId)<<" digit: "<<digitStrip);
162 return false;
163 }
164 constexpr double dummyCharge = 66666;
185 const double pull = (smearedX - (*design.center(digitStrip)).x()) / design.stripPitch();
186 const double w1 = CLHEP::RandFlat::shoot(rndEngine, 0., 0.5 *(1. - pull));
187 const double w2 = 1. - pull -2.*w1;
188 const double w3 = pull + w1;
189
190 if (digitStrip> 1) {
191 const Identifier stripIdB = idHelper.channelID(hitId, readOutEle->multilayer(),
192 gasGap, channelType::Strip, digitStrip -1);
193 outCollection.push_back(std::make_unique<sTgcDigit>(stripIdB,
194 associateBCIdTag(ctx, timedHit),
195 hitTime(timedHit), dummyCharge * w1, false, false));
196 }
197 outCollection.push_back(std::make_unique<sTgcDigit>(digitId,
198 associateBCIdTag(ctx, timedHit),
199 hitTime(timedHit), dummyCharge * w2, false, false));
200
201 if (digitStrip + 1 <= design.numStrips()) {
202 const Identifier stripIdA = idHelper.channelID(hitId, readOutEle->multilayer(),
203 gasGap, channelType::Strip, digitStrip + 1);
204
205 outCollection.push_back(std::make_unique<sTgcDigit>(stripIdA,
206 associateBCIdTag(ctx, timedHit),
207 hitTime(timedHit), dummyCharge * w3, false, false));
208 }
209 ++m_acceptedHits[channelType::Strip];
210 return true;
211 }
212
213 bool sTgcFastDigiTool::digitizeWire(const EventContext& ctx,
214 const TimedHit& timedHit,
215 const Muon::DigitEffiData* efficiencyMap,
216 CLHEP::HepRandomEngine* rndEngine,
217 sTgcDigitCollection& outCollection) const {
218
219 if (!m_digitizeWire) {
220 return false;
221 }
222
223 ++m_allHits[channelType::Wire];
224
225 const Identifier hitId{timedHit->identify()};
227 if (efficiencyMap && efficiencyMap->getEfficiency(hitId) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
228 ATH_MSG_VERBOSE("Simulated wire hit "<<xAOD::toEigen(timedHit->localPosition())
229 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
230 return false;
231 }
232 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
233 const MuonGMR4::sTgcReadoutElement* readOutEle = m_detMgr->getsTgcReadoutElement(hitId);
234 const IdentifierHash hitHash = readOutEle->measurementHash(hitId);
235 const int gasGap = idHelper.gasGap(hitId);
236
237 const Amg::Vector2D wirePos = readOutEle->stripLayer(hitHash).to2D(xAOD::toEigen(timedHit->localPosition()), true);
238 // do not digitise wires that are never read out in reality
239 bool isInnerQ1 = readOutEle->isEtaZero(hitHash, wirePos);
240 if(isInnerQ1) {
241 return false;
242 }
244 if (efficiencyMap && efficiencyMap->getEfficiency(hitId, isInnerQ1) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
245 ATH_MSG_VERBOSE("Simulated wire hit "<<xAOD::toEigen(timedHit->localPosition())
246 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
247 return false;
248 }
249
250 const MuonGMR4::WireGroupDesign& design{readOutEle->wireDesign(hitHash)};
251
252 const int wireGrpNum = design.stripNumber(wirePos);
253 if (wireGrpNum < 0) {
254 ATH_MSG_VERBOSE("The wire "<<Amg::toString(wirePos)<<" in "<<m_idHelperSvc->toStringGasGap(hitId)
255 <<" is outside of the acceptance of "<<std::endl<<design);
256 return false;
257 }
258 const double uncert = design.stripPitch() * design.numWiresInGroup(wireGrpNum);
259
260 const double smearedX = CLHEP::RandGaussZiggurat::shoot(rndEngine, wirePos.x(), uncert);
261
262 const Amg::Vector2D digitPos{smearedX * Amg::Vector2D::UnitX()};
263
264 const int digitWire = design.stripNumber(digitPos);
265 if (digitWire < 0) {
266 ATH_MSG_VERBOSE("Strip hit "<<Amg::toString(digitPos)<<" "<<m_idHelperSvc->toStringGasGap(hitId)
267 <<" is out of range "<<std::endl<<design);
268 return false;
269 }
270 bool isValid{false};
271 const Identifier digitId = idHelper.channelID(hitId, readOutEle->multilayer(),
272 gasGap, channelType::Wire, digitWire, isValid);
273
274 if (!isValid) {
275 ATH_MSG_WARNING("Failed to deduce a valid identifier from "
276 <<m_idHelperSvc->toStringGasGap(hitId)<<" digit: "<<digitWire);
277 return false;
278 }
279 outCollection.push_back(std::make_unique<sTgcDigit>(digitId,
280 associateBCIdTag(ctx, timedHit),
281 hitTime(timedHit), 666, false, false));
282
283
284 ++m_acceptedHits[channelType::Wire];
285 return true;
286 }
287 int sTgcFastDigiTool::associateBCIdTag(const EventContext& /*ctx*/,
288 const TimedHit& /*timedHit*/) const {
290 return 0;
291 }
292
293 bool sTgcFastDigiTool::digitizePad(const EventContext& ctx,
294 const TimedHit& timedHit,
295 const Muon::DigitEffiData* efficiencyMap,
296 CLHEP::HepRandomEngine* rndEngine,
297 sTgcDigitCollection& outCollection) const {
298
299 if (!m_digitizePads) {
300 return false;
301 }
302
303 ++m_allHits[channelType::Pad];
304
305 const Identifier hitId{timedHit->identify()};
306 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
307 const MuonGMR4::sTgcReadoutElement* readOutEle = m_detMgr->getsTgcReadoutElement(hitId);
308 const int gasGap = idHelper.gasGap(hitId);
309
310 const IdentifierHash hitHash = readOutEle->createHash(gasGap, sTgcIdHelper::sTgcChannelTypes::Pad, 1);
311
312
313 const Amg::Vector2D padPos{readOutEle->stripLayer(hitHash).to2D(xAOD::toEigen(timedHit->localPosition()), true)};
315 const MuonGMR4::PadDesign& design{readOutEle->padDesign(hitHash)};
316
317 const auto [padEta, padPhi] = design.channelNumber(padPos);
318 if (padEta < 0 || padPhi < 0) {
319 ATH_MSG_VERBOSE("The pad "<<Amg::toString(padPos)<<" in "<<m_idHelperSvc->toStringGasGap(hitId)
320 <<" is outside of the acceptance of "<<std::endl<<design);
321 return false;
322 }
323 bool isValid{false};
324 const Identifier padId = idHelper.padID(hitId, readOutEle->multilayer(),
325 gasGap, channelType::Pad, padEta, padPhi, isValid);
326
327
328 if (!isValid) {
329 ATH_MSG_WARNING("Failed to decuce a valid pad Identifier from "<<Amg::toString(padPos)
330 <<" in "<<m_idHelperSvc->toStringGasGap(hitId));
331 return false;
332 }
333
335 bool isInnerQ1 = readOutEle->isEtaZero(readOutEle->measurementHash(hitId), padPos);
336 if (efficiencyMap && efficiencyMap->getEfficiency(hitId, isInnerQ1) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
337 ATH_MSG_VERBOSE("Simulated pad hit "<<xAOD::toEigen(timedHit->localPosition())
338 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
339 return false;
340 }
341
342 outCollection.push_back(std::make_unique<sTgcDigit>(padId,
343 associateBCIdTag(ctx, timedHit),
344 hitTime(timedHit), 666, false, false));
345
346 ++m_acceptedHits[channelType::Pad];
347
348 return true;
349 }
350
351
352}
#define M_PI
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
bool isValid(const T &p)
Av: we implement here an ATLAS-sepcific convention: all particles which are 99xxxxx are fine.
Definition AtlasPID.h:878
ATLAS-specific HepMC functions.
#define x
const T * back() const
Access the last element in the collection as an rvalue.
value_type push_back(value_type pElem)
Add an element to the end of the collection.
This is a "hash" representation of an Identifier.
Identifier identify() const
Definition MuonDigit.h:30
std::pair< int, int > channelNumber(const Amg::Vector2D &hitPos) const
Function to retrieve the pad eta and phi given a local position coordinate.
CheckVector2D center(int stripNumb) const
Returns the bisector of the strip (Global numbering scheme)
double stripPitch() const
Distance between two adjacent strips.
virtual int stripNumber(const Amg::Vector2D &pos) const
Calculates the number of the strip whose center is closest to the given point.
virtual int numStrips() const
Number of strips on the panel.
Amg::Vector2D to2D(const Amg::Vector3D &vec, const bool phiView) const
Transforms a 3D vector from the strip design into a 2D vector.
int stripNumber(const Amg::Vector2D &pos) const override
Calculates the number of the strip whose center is closest to the given point.
unsigned int numWiresInGroup(unsigned int groupNum) const
Returns the number of wires in a given group.
IdentifierHash measurementHash(const Identifier &measId) const override final
Constructs the identifier hash from the full measurement Identifier.
const PadDesign & padDesign(const IdentifierHash &measHash) const
Retrieves the readoutElement Layer given the Identifier/Hash.
int multilayer() const
Returns the multilayer of the sTgcReadoutElement.
const StripDesign & stripDesign(const IdentifierHash &measHash) const
Retrieves the readoutElement Layer given the Identifier/Hash.
bool isEtaZero(const IdentifierHash &measurementHash, const Amg::Vector2D &localPosition) const
const WireGroupDesign & wireDesign(const IdentifierHash &measHash) const
Retrieves the readoutElement Layer given the Identifier/Hash.
static IdentifierHash createHash(const unsigned gasGap, const unsigned channelType, const unsigned channel, const unsigned wireInGrp=0)
Create a measurement hash from the Identifier fields.
const StripLayer & stripLayer(const IdentifierHash &measId) const
size_type module_hash_max() const
the maximum hash value
xAOD::MuonSimHit * addSDO(const TimedHit &hit, xAOD::MuonSimHitContainer *sdoContainer) const
Adds the timed simHit to the output SDO container.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
std::vector< TimedHitPtr< xAOD::MuonSimHit > > TimedHits
CLHEP::HepRandomEngine * getRandomEngine(const EventContext &ctx) const
TimedHitPtr< xAOD::MuonSimHit > TimedHit
DigitColl * fetchCollection(const Identifier &hitId, OutDigitCache_t< DigitColl > &digitCache) const
Helper function that provides fetches the proper DigitCollection from the DigitCache for a given hit ...
const MuonGMR4::MuonDetectorManager * m_detMgr
StatusCode writeDigitContainer(const EventContext &ctx, const SG::WriteHandleKey< DigitCont > &key, OutDigitCache_t< DigitColl > &&digitCache, unsigned int hashMax) const
Helper function to move the collected digits into the final DigitContainer.
static double hitTime(const TimedHit &hit)
Returns the global time of the hit which is the sum of eventTime & individual hit time.
SG::ReadCondHandleKey< Muon::DigitEffiData > m_effiDataKey
StatusCode initialize() override final
Gaudi::Property< double > m_limitElectronKineticEnergy
bool digitizeStrip(const EventContext &ctx, const TimedHit &timedHit, const NswErrorCalibData *errorCalibDB, const Muon::DigitEffiData *effiData, CLHEP::HepRandomEngine *rndEngine, sTgcDigitCollection &outCollection) const
StatusCode finalize() override final
Gaudi::Property< bool > m_digitizeStrip
Gaudi::Property< bool > m_digitizePads
Gaudi::Property< double > m_energyDepositThreshold
Gaudi::Property< bool > m_digitizeMuonOnly
SG::WriteHandleKey< sTgcDigitContainer > m_writeKey
Gaudi::Property< bool > m_digitizeWire
SG::ReadCondHandleKey< NswErrorCalibData > m_uncertCalibKey
bool digitizePad(const EventContext &ctx, const TimedHit &timedHit, const Muon::DigitEffiData *effiData, CLHEP::HepRandomEngine *rndEngine, sTgcDigitCollection &outCollection) const
OutDigitCache_t< sTgcDigitCollection > DigiCache
StatusCode digitize(const EventContext &ctx, const TimedHits &hitsToDigit, xAOD::MuonSimHitContainer *sdoContainer) const override final
Digitize the time ordered hits and write them to the digit format specific for the detector technolog...
bool digitizeWire(const EventContext &ctx, const TimedHit &timedHit, const Muon::DigitEffiData *effiData, CLHEP::HepRandomEngine *rndEngine, sTgcDigitCollection &outCollection) const
int associateBCIdTag(const EventContext &ctx, const TimedHit &timedHit) const
: Associates the global bcIdTag to the digit
double getEfficiency(const Identifier &channelId, bool isInnerQ1=false) const
Returns the signal generation efficiency of the sTgc channel.
double clusterUncertainty(const Input &clustInfo) const
Helper class to provide type-safe access to aux data.
Definition Decorator.h:59
int channel(const Identifier &id) const override
Identifier padID(int stationName, int stationEta, int stationPhi, int multilayer, int gasGap, int channelType, int padEta, int padPhi) const
int gasGap(const Identifier &id) const override
get the hashes
Identifier channelID(int stationName, int stationEta, int stationPhi, int multilayer, int gasGap, int channelType, int channel) const
bool next() noexcept
Loads the hits from the next chamber.
ConstVectorMap< 3 > localDirection() const
Returns the local direction of the traversing particle.
void setIdentifier(const Identifier &id)
Sets the global ATLAS identifier.
Identifier identify() const
Returns the global ATLAS identifier of the SimHit.
ConstVectorMap< 3 > localPosition() const
Returns the local postion of the traversing particle.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Matrix< double, 2, 1 > Vector2D
bool isElectron(const T &p)
bool isMuon(const T &p)
This header ties the generic definitions in this package.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
MuonSimHit_v1 MuonSimHit
Defined the version of the MuonSimHit.
Definition MuonSimHit.h:12
MuonSimHitContainer_v1 MuonSimHitContainer
Define the version of the pixel cluster container.
Helper struct to be parsed to the object to derive the specific error of the cluster.
double locTheta
Direction of the muon in the local coordinate frame.
Identifier stripId
Identifier of the strip.
uint8_t clusterAuthor
Author of the cluster.