ATLAS Offline Software
Loading...
Searching...
No Matches
sTgcFastDigiTool.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 "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 using ChVec_t = std::vector<std::uint16_t>;
16 static const SG::Decorator<ChVec_t> dec_stripCh{"SDO_etaChannels"};
17 static const SG::Decorator<ChVec_t> dec_wireCh{"SDO_phiChannels"};
18 static const SG::Decorator<ChVec_t> dec_padCh{"SDO_padChannels"};
19
20}
21namespace MuonR4 {
22
25 ATH_CHECK(m_writeKey.initialize());
26 ATH_CHECK(m_effiDataKey.initialize(!m_effiDataKey.empty()));
27 ATH_CHECK(m_uncertCalibKey.initialize());
28 return StatusCode::SUCCESS;
29 }
31 ATH_MSG_INFO("Tried to convert "<<m_allHits[channelType::Strip]<<"/"
32 <<m_allHits[channelType::Wire]<<"/"
33 <<m_allHits[channelType::Pad]<<" strip/wire/pad hits. In, "
34 <<percentage(m_acceptedHits[channelType::Strip], m_allHits[channelType::Strip]) <<"/"
35 <<percentage(m_acceptedHits[channelType::Wire], m_allHits[channelType::Wire])<<"/"
36 <<percentage(m_acceptedHits[channelType::Pad], m_allHits[channelType::Pad])
37 <<"% of the cases, the conversion was successful");
38 return StatusCode::SUCCESS;
39 }
40 StatusCode sTgcFastDigiTool::digitize(const EventContext& ctx,
41 const TimedHits& hitsToDigit,
42 xAOD::MuonSimHitContainer* sdoContainer) const {
43 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
44 // Prepare the temporary cache
45 DigiCache digitCache{};
47 const Muon::DigitEffiData* efficiencyMap{nullptr};
48 ATH_CHECK(SG::get(efficiencyMap, m_effiDataKey, ctx));
49 const NswErrorCalibData* nswUncertDB{nullptr};
50 ATH_CHECK(SG::get(nswUncertDB, m_uncertCalibKey, ctx));
51
52 CLHEP::HepRandomEngine* rndEngine = getRandomEngine(ctx);
53 xAOD::ChamberViewer viewer{hitsToDigit, m_idHelperSvc.get()};
54 do {
55 for (const TimedHit& simHit : viewer) {
57 if (m_digitizeMuonOnly && !MC::isMuon(simHit)){
58 continue;
59 }
60
61 if (simHit->energyDeposit() < m_energyDepositThreshold){
62 ATH_MSG_VERBOSE("Hit with Energy Deposit of " << simHit->energyDeposit()
63 << " less than " << m_energyDepositThreshold << ". Skip this hit." );
64 continue;
65 }
66
67 const double hitKineticEnergy = simHit->kineticEnergy();
68 if (hitKineticEnergy < m_limitElectronKineticEnergy && MC::isElectron(simHit)) {
69 ATH_MSG_DEBUG("Skip electron hit with kinetic energy " << hitKineticEnergy
70 << ", which is less than the lower limit of " << m_limitElectronKineticEnergy);
71 continue;
72 }
73 sTgcDigitCollection* digiColl = fetchCollection(simHit->identify(), digitCache);
74 const bool digitizedPad = digitizePad(ctx, simHit, efficiencyMap, rndEngine, *digiColl);
75 const std::int16_t padChannel = digitizedPad ? idHelper.channel(digiColl->back()->identify()) : -1;
76 const bool digitizedWire = digitizeWire(ctx, simHit, efficiencyMap, rndEngine, *digiColl);
77 const std::int16_t wireChannel = digitizedWire ? idHelper.channel(digiColl->back()->identify()) : -1;
78 const bool digitizedStrip = digitizeStrip(ctx, simHit, nswUncertDB, efficiencyMap, rndEngine, *digiColl);
79 const std::int16_t stripCh = digitizedStrip ? idHelper.channel(digiColl->back()->identify()) : -1;
80
81 if (digitizedStrip || digitizedPad || digitizedWire) {
82 xAOD::MuonSimHit* sdo = addSDO(simHit, sdoContainer);
83 ChVec_t& stripChV{dec_stripCh(*sdo)};
84 ChVec_t& wireChV{dec_wireCh(*sdo)};
85 ChVec_t& padChV{dec_padCh(*sdo)};
86 if (stripCh > 0) { stripChV.push_back(stripCh); }
87 if (wireChannel > 0) { wireChV.push_back(wireChannel); }
88 if (padChannel > 0) { padChV.push_back(padChannel); }
89 sdo->setIdentifier(digiColl->back()->identify());
90 }
91 }
92 } while (viewer.next());
94 ATH_CHECK(writeDigitContainer(ctx, m_writeKey, std::move(digitCache),
95 idHelper.module_hash_max()));
96 return StatusCode::SUCCESS;
97 }
98 bool sTgcFastDigiTool::digitizeStrip(const EventContext& ctx,
99 const TimedHit& timedHit,
100 const NswErrorCalibData* errorCalibDB,
101 const Muon::DigitEffiData* efficiencyMap,
102 CLHEP::HepRandomEngine* rndEngine,
103 sTgcDigitCollection& outCollection) const {
104
105 if (!m_digitizeStrip) {
106 return false;
107 }
108 ++m_allHits[channelType::Strip];
109 const Identifier hitId{timedHit->identify()};
110 const MuonGMR4::sTgcReadoutElement* readOutEle{m_detMgr->getsTgcReadoutElement(hitId)};
111
112 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
113
114 const IdentifierHash meashHash = readOutEle->measurementHash(hitId);
115 const MuonGMR4::StripDesign& design{readOutEle->stripDesign(meashHash)};
116
117 const Amg::Vector2D stripPos = readOutEle->stripLayer(meashHash)
118 .to2D(xAOD::toEigen(timedHit->localPosition()), false);
119
120 const int stripNum = design.stripNumber(stripPos);
121 if (stripNum < 0) {
122 ATH_MSG_VERBOSE("Strip hit "<<Amg::toString(stripPos)<<" "<<m_idHelperSvc->toStringGasGap(hitId)
123 <<" is out of range "<<std::endl<<design);
124 return false;
125 }
126
127
128 bool isValid{false};
129 const int gasGap = idHelper.gasGap(hitId);
130 const Identifier stripId = idHelper.channelID(hitId, readOutEle->multilayer(),
131 gasGap, channelType::Strip, stripNum, isValid);
132
133 if (!isValid) {
134 ATH_MSG_WARNING("Failed to deduce a valid identifier from "
135 <<m_idHelperSvc->toStringGasGap(hitId)<<" strip: "<<stripNum);
136 return false;
137 }
138
140 bool isInnerQ1 = readOutEle->isEtaZero(readOutEle->measurementHash(hitId), stripPos);
141 if (efficiencyMap && efficiencyMap->getEfficiency(hitId, isInnerQ1) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
142 ATH_MSG_VERBOSE("Simulated strip hit "<<xAOD::toEigen(timedHit->localPosition())
143 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
144 return false;
145 }
146
147 NswErrorCalibData::Input errorCalibInput{};
148 errorCalibInput.stripId= stripId;
149 errorCalibInput.locTheta = M_PI - timedHit->localDirection().theta();
150 errorCalibInput.clusterAuthor = 3; // centroid
151
152 const double uncert = errorCalibDB->clusterUncertainty(errorCalibInput);
153 const double smearedX = CLHEP::RandGaussZiggurat::shoot(rndEngine, stripPos.x(), uncert);
154
155 const Amg::Vector2D digitPos{smearedX * Amg::Vector2D::UnitX()};
156
157 const int digitStrip = design.stripNumber(digitPos);
158 if (digitStrip < 0) {
159 ATH_MSG_VERBOSE("Smeared strip hit "<<Amg::toString(digitPos)<<" "<<m_idHelperSvc->toStringGasGap(hitId)
160 <<" is out of range "<<std::endl<<design);
161 return false;
162 }
163 const Identifier digitId = idHelper.channelID(hitId, readOutEle->multilayer(),
164 gasGap, channelType::Strip, digitStrip, isValid);
165
166 if (!isValid) {
167 ATH_MSG_WARNING("Failed to deduce a valid identifier from "
168 <<m_idHelperSvc->toStringGasGap(hitId)<<" digit: "<<digitStrip);
169 return false;
170 }
171 constexpr double dummyCharge = 66666;
192 const double pull = (smearedX - (*design.center(digitStrip)).x()) / design.stripPitch();
193 const double w1 = CLHEP::RandFlat::shoot(rndEngine, 0., 0.5 *(1. - pull));
194 const double w2 = 1. - pull -2.*w1;
195 const double w3 = pull + w1;
196
197 if (digitStrip> 1) {
198 const Identifier stripIdB = idHelper.channelID(hitId, readOutEle->multilayer(),
199 gasGap, channelType::Strip, digitStrip -1);
200 outCollection.push_back(std::make_unique<sTgcDigit>(stripIdB,
201 associateBCIdTag(ctx, timedHit),
202 hitTime(timedHit), dummyCharge * w1, false, false));
203 }
204 outCollection.push_back(std::make_unique<sTgcDigit>(digitId,
205 associateBCIdTag(ctx, timedHit),
206 hitTime(timedHit), dummyCharge * w2, false, false));
207
208 if (digitStrip + 1 <= design.numStrips()) {
209 const Identifier stripIdA = idHelper.channelID(hitId, readOutEle->multilayer(),
210 gasGap, channelType::Strip, digitStrip + 1);
211
212 outCollection.push_back(std::make_unique<sTgcDigit>(stripIdA,
213 associateBCIdTag(ctx, timedHit),
214 hitTime(timedHit), dummyCharge * w3, false, false));
215 }
216 ++m_acceptedHits[channelType::Strip];
217 return true;
218 }
219
220 bool sTgcFastDigiTool::digitizeWire(const EventContext& ctx,
221 const TimedHit& timedHit,
222 const Muon::DigitEffiData* efficiencyMap,
223 CLHEP::HepRandomEngine* rndEngine,
224 sTgcDigitCollection& outCollection) const {
225
226 if (!m_digitizeWire) {
227 return false;
228 }
229
230 ++m_allHits[channelType::Wire];
231
232 const Identifier hitId{timedHit->identify()};
234 if (efficiencyMap && efficiencyMap->getEfficiency(hitId) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
235 ATH_MSG_VERBOSE("Simulated wire hit "<<xAOD::toEigen(timedHit->localPosition())
236 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
237 return false;
238 }
239 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
240 const MuonGMR4::sTgcReadoutElement* readOutEle = m_detMgr->getsTgcReadoutElement(hitId);
241 const IdentifierHash hitHash = readOutEle->measurementHash(hitId);
242 const int gasGap = idHelper.gasGap(hitId);
243
244 const Amg::Vector2D wirePos = readOutEle->stripLayer(hitHash).to2D(xAOD::toEigen(timedHit->localPosition()), true);
245 // do not digitise wires that are never read out in reality
246 bool isInnerQ1 = readOutEle->isEtaZero(hitHash, xAOD::toEigen(timedHit->localPosition()).block<2,1>(0,0));
247 if(isInnerQ1) {
248 return false;
249 }
251 if (efficiencyMap && efficiencyMap->getEfficiency(hitId, isInnerQ1) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
252 ATH_MSG_VERBOSE("Simulated wire hit "<<xAOD::toEigen(timedHit->localPosition())
253 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
254 return false;
255 }
256
257 const MuonGMR4::WireGroupDesign& design{readOutEle->wireDesign(hitHash)};
258
259 const int wireGrpNum = design.stripNumber(wirePos);
260 if (wireGrpNum < 0) {
261 ATH_MSG_VERBOSE("The wire "<<Amg::toString(wirePos)<<" in "<<m_idHelperSvc->toStringGasGap(hitId)
262 <<" is outside of the acceptance of "<<std::endl<<design);
263 return false;
264 }
265 const double uncert = design.stripPitch() * design.numWiresInGroup(wireGrpNum);
266
267 const double smearedX = CLHEP::RandGaussZiggurat::shoot(rndEngine, wirePos.x(), uncert);
268
269 const Amg::Vector2D digitPos{smearedX * Amg::Vector2D::UnitX()};
270
271 const int digitWire = design.stripNumber(digitPos);
272 if (digitWire < 0) {
273 ATH_MSG_VERBOSE("Strip hit "<<Amg::toString(digitPos)<<" "<<m_idHelperSvc->toStringGasGap(hitId)
274 <<" is out of range "<<std::endl<<design);
275 return false;
276 }
277 bool isValid{false};
278 const Identifier digitId = idHelper.channelID(hitId, readOutEle->multilayer(),
279 gasGap, channelType::Wire, digitWire, isValid);
280
281 if (!isValid) {
282 ATH_MSG_WARNING("Failed to deduce a valid identifier from "
283 <<m_idHelperSvc->toStringGasGap(hitId)<<" digit: "<<digitWire);
284 return false;
285 }
286 outCollection.push_back(std::make_unique<sTgcDigit>(digitId,
287 associateBCIdTag(ctx, timedHit),
288 hitTime(timedHit), 666, false, false));
289
290
291 ++m_acceptedHits[channelType::Wire];
292 return true;
293 }
294 int sTgcFastDigiTool::associateBCIdTag(const EventContext& /*ctx*/,
295 const TimedHit& /*timedHit*/) const {
297 return 0;
298 }
299
300 bool sTgcFastDigiTool::digitizePad(const EventContext& ctx,
301 const TimedHit& timedHit,
302 const Muon::DigitEffiData* efficiencyMap,
303 CLHEP::HepRandomEngine* rndEngine,
304 sTgcDigitCollection& outCollection) const {
305
306 if (!m_digitizePads) {
307 return false;
308 }
309
310 ++m_allHits[channelType::Pad];
311
312 const Identifier hitId{timedHit->identify()};
313 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
314 const MuonGMR4::sTgcReadoutElement* readOutEle = m_detMgr->getsTgcReadoutElement(hitId);
315 const int gasGap = idHelper.gasGap(hitId);
316
317 const IdentifierHash hitHash = readOutEle->createHash(gasGap, sTgcIdHelper::sTgcChannelTypes::Pad, 1);
318
319
320 const Amg::Vector2D padPos{readOutEle->stripLayer(hitHash).to2D(xAOD::toEigen(timedHit->localPosition()), true)};
322 const MuonGMR4::PadDesign& design{readOutEle->padDesign(hitHash)};
323
324 const auto [padEta, padPhi] = design.channelNumber(padPos);
325 if (padEta <= 0 || padPhi <= 0) {
326 ATH_MSG_VERBOSE("The pad "<<Amg::toString(padPos)<<" in "<<m_idHelperSvc->toStringGasGap(hitId)
327 <<" is outside of the acceptance of "<<std::endl<<design);
328 return false;
329 }
330 bool isValid{false};
331 const Identifier padId = idHelper.padID(hitId, readOutEle->multilayer(),
332 gasGap, channelType::Pad, padEta, padPhi, isValid);
333
334
335 if (!isValid) {
336 ATH_MSG_WARNING("Failed to deduce a valid pad Identifier from "<<Amg::toString(padPos)
337 <<" in "<<m_idHelperSvc->toStringGasGap(hitId));
338 return false;
339 }
340
342 bool isInnerQ1 = readOutEle->isEtaZero(readOutEle->measurementHash(hitId), xAOD::toEigen(timedHit->localPosition()).block<2,1>(0,0));
343 if (efficiencyMap && efficiencyMap->getEfficiency(hitId, isInnerQ1) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)){
344 ATH_MSG_VERBOSE("Simulated pad hit "<<xAOD::toEigen(timedHit->localPosition())
345 << m_idHelperSvc->toString(hitId) <<" is rejected because of efficency modelling");
346 return false;
347 }
348
349 outCollection.push_back(std::make_unique<sTgcDigit>(padId,
350 associateBCIdTag(ctx, timedHit),
351 hitTime(timedHit), 666, false, false));
352
353 ++m_acceptedHits[channelType::Pad];
354
355 return true;
356 }
357
358
359}
#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.