ATLAS Offline Software
Loading...
Searching...
No Matches
RpcFastDigiTool.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 "RpcFastDigiTool.h"
5
9#include "CLHEP/Random/RandGaussZiggurat.h"
10#include "CLHEP/Random/RandFlat.h"
11namespace {
12 constexpr double percentage(unsigned int numerator, unsigned int denom) {
13 return 100. * numerator / std::max(denom, 1u);
14 }
15 using ChVec_t = std::vector<std::uint16_t>;
17 static const SG::Decorator<ChVec_t> dec_phiChannel{"SDO_phiChannels"};
18 static const SG::Decorator<ChVec_t> dec_etaChannel{"SDO_etaChannels"};
19}
20namespace MuonR4 {
21
24 ATH_CHECK(m_writeKey.initialize());
25 ATH_CHECK(m_effiDataKey.initialize(!m_effiDataKey.empty()));
26 return StatusCode::SUCCESS;
27 }
29 ATH_MSG_INFO("Tried to convert "<<m_allHits[0]<<"/"<<m_allHits[1]<<" hits. In, "
30 <<percentage(m_acceptedHits[0], m_allHits[0]) <<"/"
31 <<percentage(m_acceptedHits[1], m_allHits[1]) <<"% of the cases, the conversion was successful");
32 return StatusCode::SUCCESS;
33 }
34 StatusCode RpcFastDigiTool::digitize(const EventContext& ctx,
35 const TimedHits& hitsToDigit,
36 xAOD::MuonSimHitContainer* sdoContainer) const {
37 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
38 // Prepare the temporary cache
39 DigiCache digitCache{};
41 const Muon::DigitEffiData* efficiencyMap{nullptr};
42 ATH_CHECK(SG::get(efficiencyMap, m_effiDataKey, ctx));
43
44 CLHEP::HepRandomEngine* rndEngine = getRandomEngine(ctx);
45 xAOD::ChamberViewer viewer{hitsToDigit, m_idHelperSvc.get()};
46 do {
47 DeadTimeMap deadTimes{};
48 for (const TimedHit& simHit : viewer) {
49 if (m_digitizeMuonOnly && !MC::isMuon(simHit)){
50 continue;
51 }
52 const Identifier hitId{simHit->identify()};
53 RpcDigitCollection* digiColl = fetchCollection(hitId, digitCache);
54 //The new BI chambers only have eta strips
55 bool run4_BI = m_detMgr->getRpcReadoutElement(hitId)->nPhiStrips() == 0;
56 if (!run4_BI) {
58 const bool digitizedPhi = digitizeHit(simHit, true, efficiencyMap, *digiColl, rndEngine, deadTimes);
59 std::int16_t phiChannel = digitizedPhi ? idHelper.channel(digiColl->back()->identify()) : -1;
60 const bool digitizedEta = digitizeHit(simHit, false, efficiencyMap, *digiColl, rndEngine, deadTimes);
61
62 if (digitizedEta || digitizedPhi) {
63 xAOD::MuonSimHit* sdo = addSDO(simHit, sdoContainer);
64 sdo->setIdentifier(digiColl->back()->identify());
65 ChVec_t& phiChs{dec_phiChannel(*sdo)};
66 if (digitizedPhi) {
67 phiChs.push_back(phiChannel);
68 }
69 dec_etaChannel(*sdo).clear();
70 }
71 } else if (digitizeHitBI(simHit, efficiencyMap, *digiColl, rndEngine, deadTimes)) {
72 xAOD::MuonSimHit* sdo = addSDO(simHit, sdoContainer);
73 sdo->setIdentifier(digiColl->back()->identify());
74 dec_etaChannel(*sdo).clear();
75 dec_phiChannel(*sdo).clear();
76 }
77 }
78 } while (viewer.next());
80 ATH_CHECK(writeDigitContainer(ctx, m_writeKey, std::move(digitCache), idHelper.module_hash_max()));
81 return StatusCode::SUCCESS;
82 }
84 const bool measuresPhi,
85 const Muon::DigitEffiData* effiMap,
86 RpcDigitCollection& outContainer,
87 CLHEP::HepRandomEngine* rndEngine,
88 DeadTimeMap& deadTimes) const {
89
90 ++(m_allHits[measuresPhi]);
91
92 const Identifier gasGapId = hit->identify();
93 const MuonGMR4::RpcReadoutElement* reEle = m_detMgr->getRpcReadoutElement(gasGapId);
94
95 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
96
97 bool isValid{false};
98 const Identifier layerId = idHelper.channelID(gasGapId,
99 idHelper.doubletZ(gasGapId),
100 idHelper.doubletPhi(gasGapId),
101 idHelper.gasGap(gasGapId),
102 measuresPhi, 1, isValid);
103
104
105 const MuonGMR4::StripLayerPtr& layerDesign = reEle->sensorLayout(reEle->layerHash(layerId));
106
107 const MuonGMR4::StripDesign& design{layerDesign->design(measuresPhi)};
108
109 const double uncert = design.stripPitch() / std::sqrt(12.);
110 Amg::Vector3D locHitPos{xAOD::toEigen(hit->localPosition())};
111 locHitPos[measuresPhi] = CLHEP::RandGaussZiggurat::shoot(rndEngine, locHitPos[measuresPhi], uncert);
112
113 const Amg::Vector2D locPos2D = layerDesign->to2D(locHitPos, measuresPhi);
114 if (!design.insideTrapezoid(locPos2D)) {
115 ATH_MSG_VERBOSE("The hit "<<Amg::toString(locHitPos)<<" / "<<Amg::toString(locPos2D)
116 <<" is outside of the trapezoid bounds for "<<m_idHelperSvc->toString(layerId));
117 return false;
118 }
119 const int strip = design.stripNumber(locPos2D);
120 if (strip < 0) {
121 ATH_MSG_VERBOSE("Hit " << Amg::toString(locHitPos) <<" / "<<Amg::toString(locPos2D)
122 << " cannot trigger any signal in a strip for " << m_idHelperSvc->toString(layerId)
123 <<std::endl<<design);
124 return false;
125 }
126
127 const Identifier digitId{idHelper.channelID(gasGapId,
128 idHelper.doubletZ(gasGapId),
129 idHelper.doubletPhi(gasGapId),
130 idHelper.gasGap(gasGapId),
131 measuresPhi, strip, isValid)};
132
133 if (!isValid) {
134 ATH_MSG_WARNING("Invalid hit identifier obtained for "<<m_idHelperSvc->toStringGasGap(gasGapId)
135 <<", eta strip "<<strip<<" & hit "<<Amg::toString(locHitPos,2 )
136 <<" /// "<<design);
137 return false;
138 }
140 if (effiMap && effiMap->getEfficiency(digitId) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)) {
141 ATH_MSG_VERBOSE("Hit is marked as inefficient");
142 return false;
143 }
144 if (!passDeadTime(digitId, hitTime(hit), m_deadTime, deadTimes)) {
145 ATH_MSG_VERBOSE("Reject hit due to dead map constraint");
146 return false;
147 }
149 const double signalTime = hitTime(hit) + reEle->distanceToEdge(reEle->measurementHash(digitId),
150 locHitPos, EdgeSide::readOut) / m_propagationVelocity;
151 const double digitTime = CLHEP::RandGaussZiggurat::shoot(rndEngine, signalTime, m_stripTimeResolution);
152 ATH_MSG_VERBOSE("Created new digit "<<m_idHelperSvc->toString(digitId)<<", @ "<<Amg::toString(locPos2D)
153 <<", recorded time: "<<digitTime);
154 outContainer.push_back(std::make_unique<RpcDigit>(digitId, digitTime, timeOverThreshold(rndEngine)));
155 ++(m_acceptedHits[measuresPhi]);
156 return true;
157 }
159 const Muon::DigitEffiData* effiMap,
160 RpcDigitCollection& outContainer,
161 CLHEP::HepRandomEngine* rndEngine,
162 DeadTimeMap& deadTimes) const {
163
164 ++(m_allHits[false]);
165 const Identifier gasGapId = simHit->identify();
166 const MuonGMR4::RpcReadoutElement* reEle = m_detMgr->getRpcReadoutElement(gasGapId);
167 const Amg::Vector3D locPos = xAOD::toEigen(simHit->localPosition());
168 const MuonGMR4::StripDesign& design{*reEle->getParameters().etaDesign};
169 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
170
171
173 const double uncert = design.stripPitch() / std::sqrt(12.);
174 const double smearedX = CLHEP::RandGaussZiggurat::shoot(rndEngine, locPos.x(), uncert);
175
177 // True propagation time in nanoseconds along strip to y=-stripLength/2 (L) and y=stripLength/2 (R)
178 const IdentifierHash layHash = reEle->layerHash(gasGapId);
179 const double propagationTimeL = reEle->distanceToEdge(layHash, locPos, EdgeSide::readOut) / m_propagationVelocity;
180 const double propagationTimeR = reEle->distanceToEdge(layHash, locPos, EdgeSide::highVoltage)/ m_propagationVelocity;
181
183 const double smearedTimeL = CLHEP::RandGaussZiggurat::shoot(rndEngine, propagationTimeL, m_stripTimeResolution);
184 const double smearedTimeR = CLHEP::RandGaussZiggurat::shoot(rndEngine, propagationTimeR, m_stripTimeResolution);
185
186 const double smearedDeltaT = smearedTimeR - smearedTimeL;
187
188 /*
189 |--- d1, t1 ---||--- d1, t1 ---||-- d --| For t2 > t1:
190 ||||||||||||||||X|||||||||||||||||||||||| <- RPC strip, deltaT = t2 - t1, d1 = v_prop * t1 (likewise for d2),
191 |-------- d2, t2 -------| X is a hit l = d1 + d2, d = d2 - d1 -> d = l - 2d1 = v_prop * deltaT
192 |----------------- l -------------------|
193 Hence, d1 = 0.5 * (l - d) = 0.5 * (l - v_prop * deltaT)
194
195 Then converting to coordinate system where 0 -> -0.5*l to match strip local coordinates
196 d1 -> d1 = -0.5*l + 0.5* (l-d) = -0.5*d = -0.5 * v_pro*deltaT
197 */
198 const double smearedY = 0.5 * m_propagationVelocity * smearedDeltaT; //in mm
199 //If smearedDeltaT == 0 position is in the centre of the strip (0).
200
201 const Amg::Vector2D locHitPos{smearedX, smearedY};
202
203 if (!design.insideTrapezoid(locHitPos)) {
204 ATH_MSG_VERBOSE("The hit "<<Amg::toString(locHitPos)<<" is outside of the trapezoid bounds for "
205 <<m_idHelperSvc->toStringGasGap(gasGapId));
206 return false;
207 }
208
209 const int strip = design.stripNumber(locHitPos);
210 if (strip < 0) {
211 ATH_MSG_VERBOSE("Hit " << Amg::toString(locHitPos) << " cannot trigger any signal in a strip for "
212 << m_idHelperSvc->toStringGasGap(gasGapId) <<std::endl<<design);
213 return false;
214 }
215
216
217 bool isValid{false};
218 const Identifier digitId{idHelper.channelID(gasGapId,
219 idHelper.doubletZ(gasGapId),
220 idHelper.doubletPhi(gasGapId),
221 idHelper.gasGap(gasGapId),
222 false, strip, isValid)};
223 if (!isValid) {
224 ATH_MSG_WARNING("Failed to create a valid strip "<<m_idHelperSvc->toStringGasGap(gasGapId)
225 <<", strip: "<<strip);
226 return false;
227 }
228 if (!passDeadTime(digitId, hitTime(simHit), m_deadTime, deadTimes)) {
229 ATH_MSG_VERBOSE("Reject hit due to dead map constraint");
230 return false;
231 }
232 ATH_MSG_VERBOSE("Digitize hit "<<m_idHelperSvc->toString(digitId)<<" located at: "<<Amg::toString(locPos)
233 <<", SDO: "<<Amg::toString(locHitPos));
235 const bool effiSignal1 = !effiMap || effiMap->getEfficiency(gasGapId) >= CLHEP::RandFlat::shoot(rndEngine,0., 1.);
236 const bool effiSignal2 = !effiMap || effiMap->getEfficiency(gasGapId) >= CLHEP::RandFlat::shoot(rndEngine,0., 1.);
237 if (effiSignal1) {
238 outContainer.push_back(std::make_unique<RpcDigit>(digitId, hitTime(simHit) + smearedTimeR, timeOverThreshold(rndEngine)));
239 }
240 if (effiSignal2) {
241 outContainer.push_back(std::make_unique<RpcDigit>(digitId, hitTime(simHit) + smearedTimeL, timeOverThreshold(rndEngine), true));
242 }
243 if (effiSignal1 || effiSignal2) {
244 ++(m_acceptedHits[false]);
245 return true;
246 }
247 return false;
248 }
249 double RpcFastDigiTool::timeOverThreshold(CLHEP::HepRandomEngine* rndmEngine) {
250 //mn Time-over-threshold modeled as a narrow and a wide gaussian
251 //mn based on the fit documented in https://its.cern.ch/jira/browse/ATLASRECTS-7820
252 constexpr double tot_mean_narrow = 16.;
253 constexpr double tot_sigma_narrow = 2.;
254 constexpr double tot_mean_wide = 15.;
255 constexpr double tot_sigma_wide = 4.5;
256
257 double thetot = 0.;
258
259 if (CLHEP::RandFlat::shoot(rndmEngine)<0.75) {
260 thetot = CLHEP::RandGaussZiggurat::shoot(rndmEngine, tot_mean_narrow, tot_sigma_narrow);
261 } else {
262 thetot = CLHEP::RandGaussZiggurat::shoot(rndmEngine, tot_mean_wide, tot_sigma_wide);
263 }
264
265 return std::max(thetot, 0.);
266 }
267
268}
#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)
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.
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
IdentifierHash measurementHash(const Identifier &measId) const override final
Constructs the identifier hash from the full measurement Identifier.
double distanceToEdge(const IdentifierHash &measHash, const Amg::Vector3D &posInStripPlane, const EdgeSide side) const
Returns the disance to the readout.
IdentifierHash layerHash(const Identifier &measId) const override final
The layer hash removes the bits from the IdentifierHash corresponding to the measurement's channel nu...
const StripLayerPtr & sensorLayout(const IdentifierHash &measHash) const
Access to the StripLayer associated to a given measurement Hash.
double stripPitch() const
Distance between two adjacent strips.
bool insideTrapezoid(const Amg::Vector2D &extPos) const
Checks whether an external point is inside the trapezoidal area.
virtual int stripNumber(const Amg::Vector2D &pos) const
Calculates the number of the strip whose center is closest to the given point.
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
static bool passDeadTime(const Identifier &channelId, const double hitTime, const double deadTimeWindow, DeadTimeMap &deadTimeMap)
Returns whether the new digit is within the dead time window.
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.
std::unordered_map< Identifier, double > DeadTimeMap
StatusCode finalize() override final
bool digitizeHitBI(const TimedHit &simHit, const Muon::DigitEffiData *effiMap, RpcDigitCollection &outContainer, CLHEP::HepRandomEngine *rndEngine, DeadTimeMap &deadTimes) const
Digitize the sim hit as Rpc strip 2D hit.
SG::WriteHandleKey< RpcDigitContainer > m_writeKey
Gaudi::Property< bool > m_digitizeMuonOnly
Gaudi::Property< double > m_propagationVelocity
Gaudi::Property< double > m_deadTime
static double timeOverThreshold(CLHEP::HepRandomEngine *rndmEngine)
Roll the time over threshold for each signal digit.
Gaudi::Property< double > m_stripTimeResolution
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...
OutDigitCache_t< RpcDigitCollection > DigiCache
StatusCode initialize() override final
SG::ReadCondHandleKey< Muon::DigitEffiData > m_effiDataKey
bool digitizeHit(const TimedHit &simHit, const bool measuresPhi, const Muon::DigitEffiData *effiMap, RpcDigitCollection &outContainer, CLHEP::HepRandomEngine *rndEngine, DeadTimeMap &deadTimes) const
Digitize the sim hit as Rpc strip 1D hit.
double getEfficiency(const Identifier &channelId, bool isInnerQ1=false) const
Returns the signal generation efficiency of the sTgc channel.
Identifier channelID(int stationName, int stationEta, int stationPhi, int doubletR, int doubletZ, int doubletPhi, int gasGap, int measuresPhi, int strip) const
int gasGap(const Identifier &id) const override
get the hashes
int channel(const Identifier &id) const override
int doubletPhi(const Identifier &id) const
int doubletZ(const Identifier &id) const
Helper class to provide type-safe access to aux data.
Definition Decorator.h:59
bool next() noexcept
Loads the hits from the next chamber.
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
Eigen::Matrix< double, 3, 1 > Vector3D
bool isMuon(const T &p)
GeoModel::TransientConstSharedPtr< StripLayer > StripLayerPtr
Definition StripLayer.h:100
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.