ATLAS Offline Software
Loading...
Searching...
No Matches
RpcFastDigiTool.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 "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}
16namespace MuonR4 {
17
20 ATH_CHECK(m_writeKey.initialize());
21 ATH_CHECK(m_effiDataKey.initialize(!m_effiDataKey.empty()));
22 m_stIdxBIL = m_idHelperSvc->rpcIdHelper().stationNameIndex("BIL");
23 m_stIdxBIS = m_idHelperSvc->rpcIdHelper().stationNameIndex("BIS");
24 return StatusCode::SUCCESS;
25 }
27 ATH_MSG_INFO("Tried to convert "<<m_allHits[0]<<"/"<<m_allHits[1]<<" hits. In, "
28 <<percentage(m_acceptedHits[0], m_allHits[0]) <<"/"
29 <<percentage(m_acceptedHits[1], m_allHits[1]) <<"% of the cases, the conversion was successful");
30 return StatusCode::SUCCESS;
31 }
32 StatusCode RpcFastDigiTool::digitize(const EventContext& ctx,
33 const TimedHits& hitsToDigit,
34 xAOD::MuonSimHitContainer* sdoContainer) const {
35 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
36 // Prepare the temporary cache
37 DigiCache digitCache{};
39 const Muon::DigitEffiData* efficiencyMap{nullptr};
40 ATH_CHECK(SG::get(efficiencyMap, m_effiDataKey, ctx));
41
42 CLHEP::HepRandomEngine* rndEngine = getRandomEngine(ctx);
43 xAOD::ChamberViewer viewer{hitsToDigit, m_idHelperSvc.get()};
44 do {
45 DeadTimeMap deadTimes{};
46 for (const TimedHit& simHit : viewer) {
47 if (m_digitizeMuonOnly && !MC::isMuon(simHit)){
48 continue;
49 }
50 const Identifier hitId{simHit->identify()};
51 const int stName = m_idHelperSvc->stationName(hitId);
52 RpcDigitCollection* digiColl = fetchCollection(hitId, digitCache);
53 bool run4_BI = (stName== m_stIdxBIS && std::abs(m_idHelperSvc->stationEta(hitId)) < 7) ||
55 if (!run4_BI) {
57 const bool digitizedEta = digitizeHit(simHit, false, efficiencyMap, *digiColl, rndEngine, deadTimes);
58
59 const bool digitizedPhi = digitizeHit(simHit, true, efficiencyMap, *digiColl, rndEngine, deadTimes);
60
61 if (digitizedEta || digitizedPhi) {
62 xAOD::MuonSimHit* sdo = addSDO(simHit, sdoContainer);
63 sdo->setIdentifier(digiColl->at(digiColl->size() -1)->identify());
64 }
65 } else if (digitizeHitBI(simHit, efficiencyMap, *digiColl, rndEngine, deadTimes)) {
66 xAOD::MuonSimHit* sdo = addSDO(simHit, sdoContainer);
67 sdo->setIdentifier(digiColl->at(digiColl->size() -1)->identify());
68 }
69 }
70 } while (viewer.next());
72 ATH_CHECK(writeDigitContainer(ctx, m_writeKey, std::move(digitCache), idHelper.module_hash_max()));
73 return StatusCode::SUCCESS;
74 }
76 const bool measuresPhi,
77 const Muon::DigitEffiData* effiMap,
78 RpcDigitCollection& outContainer,
79 CLHEP::HepRandomEngine* rndEngine,
80 DeadTimeMap& deadTimes) const {
81
82 ++(m_allHits[measuresPhi]);
83
84 const Identifier gasGapId = hit->identify();
85 const MuonGMR4::RpcReadoutElement* reEle = m_detMgr->getRpcReadoutElement(gasGapId);
86
87 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
88
89 bool isValid{false};
90 const Identifier layerId = idHelper.channelID(gasGapId,
91 idHelper.doubletZ(gasGapId),
92 idHelper.doubletPhi(gasGapId),
93 idHelper.gasGap(gasGapId),
94 measuresPhi, 1, isValid);
95
96
97 const MuonGMR4::StripLayerPtr& layerDesign = reEle->sensorLayout(reEle->layerHash(layerId));
98
99 const MuonGMR4::StripDesign& design{layerDesign->design(measuresPhi)};
100
101 const double uncert = design.stripPitch() / std::sqrt(12.);
102 Amg::Vector3D locHitPos{xAOD::toEigen(hit->localPosition())};
103 locHitPos[measuresPhi] = CLHEP::RandGaussZiggurat::shoot(rndEngine, locHitPos[measuresPhi], uncert);
104
105 const Amg::Vector2D locPos2D = layerDesign->to2D(locHitPos, measuresPhi);
106 if (!design.insideTrapezoid(locPos2D)) {
107 ATH_MSG_VERBOSE("The hit "<<Amg::toString(locHitPos)<<" / "<<Amg::toString(locPos2D)
108 <<" is outside of the trapezoid bounds for "<<m_idHelperSvc->toString(layerId));
109 return false;
110 }
111 const int strip = design.stripNumber(locPos2D);
112 if (strip < 0) {
113 ATH_MSG_VERBOSE("Hit " << Amg::toString(locHitPos) <<" / "<<Amg::toString(locPos2D)
114 << " cannot trigger any signal in a strip for " << m_idHelperSvc->toString(layerId)
115 <<std::endl<<design);
116 return false;
117 }
118
119 const Identifier digitId{idHelper.channelID(gasGapId,
120 idHelper.doubletZ(gasGapId),
121 idHelper.doubletPhi(gasGapId),
122 idHelper.gasGap(gasGapId),
123 measuresPhi, strip, isValid)};
124
125 if (!isValid) {
126 ATH_MSG_WARNING("Invalid hit identifier obtained for "<<m_idHelperSvc->toStringGasGap(gasGapId)
127 <<", eta strip "<<strip<<" & hit "<<Amg::toString(locHitPos,2 )
128 <<" /// "<<design);
129 return false;
130 }
132 if (effiMap && effiMap->getEfficiency(digitId) < CLHEP::RandFlat::shoot(rndEngine,0.,1.)) {
133 ATH_MSG_VERBOSE("Hit is marked as inefficient");
134 return false;
135 }
136 if (!passDeadTime(digitId, hitTime(hit), m_deadTime, deadTimes)) {
137 ATH_MSG_VERBOSE("Reject hit due to dead map constraint");
138 return false;
139 }
141 const double signalTime = hitTime(hit) + reEle->distanceToEdge(reEle->measurementHash(digitId),
142 locHitPos, EdgeSide::readOut) / m_propagationVelocity;
143 const double digitTime = CLHEP::RandGaussZiggurat::shoot(rndEngine, signalTime, m_stripTimeResolution);
144 ATH_MSG_VERBOSE("Created new digit "<<m_idHelperSvc->toString(digitId)<<", @ "<<Amg::toString(locPos2D)
145 <<", recorded time: "<<digitTime);
146 outContainer.push_back(std::make_unique<RpcDigit>(digitId, digitTime, timeOverThreshold(rndEngine)));
147 ++(m_acceptedHits[measuresPhi]);
148 return true;
149 }
151 const Muon::DigitEffiData* effiMap,
152 RpcDigitCollection& outContainer,
153 CLHEP::HepRandomEngine* rndEngine,
154 DeadTimeMap& deadTimes) const {
155
156 ++(m_allHits[false]);
157 const Identifier gasGapId = simHit->identify();
158 const MuonGMR4::RpcReadoutElement* reEle = m_detMgr->getRpcReadoutElement(gasGapId);
159 const Amg::Vector3D locPos = xAOD::toEigen(simHit->localPosition());
160 const MuonGMR4::StripDesign& design{*reEle->getParameters().etaDesign};
161 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
162
163
165 const double uncert = design.stripPitch() / std::sqrt(12.);
166 const double smearedX = CLHEP::RandGaussZiggurat::shoot(rndEngine, locPos.x(), uncert);
167
169 // True propagation time in nanoseconds along strip to y=-stripLength/2 (L) and y=stripLength/2 (R)
170 const IdentifierHash layHash = reEle->layerHash(gasGapId);
171 const double propagationTimeL = reEle->distanceToEdge(layHash, locPos, EdgeSide::readOut) / m_propagationVelocity;
172 const double propagationTimeR = reEle->distanceToEdge(layHash, locPos, EdgeSide::highVoltage)/ m_propagationVelocity;
173
175 const double smearedTimeL = CLHEP::RandGaussZiggurat::shoot(rndEngine, propagationTimeL, m_stripTimeResolution);
176 const double smearedTimeR = CLHEP::RandGaussZiggurat::shoot(rndEngine, propagationTimeR, m_stripTimeResolution);
177
178 const double smearedDeltaT = smearedTimeR - smearedTimeL;
179
180 /*
181 |--- d1, t1 ---||--- d1, t1 ---||-- d --| For t2 > t1:
182 ||||||||||||||||X|||||||||||||||||||||||| <- RPC strip, deltaT = t2 - t1, d1 = v_prop * t1 (likewise for d2),
183 |-------- d2, t2 -------| X is a hit l = d1 + d2, d = d2 - d1 -> d = l - 2d1 = v_prop * deltaT
184 |----------------- l -------------------|
185 Hence, d1 = 0.5 * (l - d) = 0.5 * (l - v_prop * deltaT)
186
187 Then converting to coordinate system where 0 -> -0.5*l to match strip local coordinates
188 d1 -> d1 = -0.5*l + 0.5* (l-d) = -0.5*d = -0.5 * v_pro*deltaT
189 */
190 const double smearedY = 0.5 * m_propagationVelocity * smearedDeltaT; //in mm
191 //If smearedDeltaT == 0 position is in the centre of the strip (0).
192
193 const Amg::Vector2D locHitPos{smearedX, smearedY};
194
195 if (!design.insideTrapezoid(locHitPos)) {
196 ATH_MSG_VERBOSE("The hit "<<Amg::toString(locHitPos)<<" is outside of the trapezoid bounds for "
197 <<m_idHelperSvc->toStringGasGap(gasGapId));
198 return false;
199 }
200
201 const int strip = design.stripNumber(locHitPos);
202 if (strip < 0) {
203 ATH_MSG_VERBOSE("Hit " << Amg::toString(locHitPos) << " cannot trigger any signal in a strip for "
204 << m_idHelperSvc->toStringGasGap(gasGapId) <<std::endl<<design);
205 return false;
206 }
207
208
209 bool isValid{false};
210 const Identifier digitId{idHelper.channelID(gasGapId,
211 idHelper.doubletZ(gasGapId),
212 idHelper.doubletPhi(gasGapId),
213 idHelper.gasGap(gasGapId),
214 false, strip, isValid)};
215 if (!isValid) {
216 ATH_MSG_WARNING("Failed to create a valid strip "<<m_idHelperSvc->toStringGasGap(gasGapId)
217 <<", strip: "<<strip);
218 return false;
219 }
220 if (!passDeadTime(digitId, hitTime(simHit), m_deadTime, deadTimes)) {
221 ATH_MSG_VERBOSE("Reject hit due to dead map constraint");
222 return false;
223 }
224 ATH_MSG_VERBOSE("Digitize hit "<<m_idHelperSvc->toString(digitId)<<" located at: "<<Amg::toString(locPos)
225 <<", SDO: "<<Amg::toString(locHitPos));
227 const bool effiSignal1 = !effiMap || effiMap->getEfficiency(gasGapId) >= CLHEP::RandFlat::shoot(rndEngine,0., 1.);
228 const bool effiSignal2 = !effiMap || effiMap->getEfficiency(gasGapId) >= CLHEP::RandFlat::shoot(rndEngine,0., 1.);
229 if (effiSignal1) {
230 outContainer.push_back(std::make_unique<RpcDigit>(digitId, hitTime(simHit) + smearedTimeR, timeOverThreshold(rndEngine)));
231 }
232 if (effiSignal2) {
233 outContainer.push_back(std::make_unique<RpcDigit>(digitId, hitTime(simHit) + smearedTimeL, timeOverThreshold(rndEngine), true));
234 }
235 if (effiSignal1 || effiSignal2) {
236 ++(m_acceptedHits[false]);
237 return true;
238 }
239 return false;
240 }
241 double RpcFastDigiTool::timeOverThreshold(CLHEP::HepRandomEngine* rndmEngine) {
242 //mn Time-over-threshold modeled as a narrow and a wide gaussian
243 //mn based on the fit documented in https://its.cern.ch/jira/browse/ATLASRECTS-7820
244 constexpr double tot_mean_narrow = 16.;
245 constexpr double tot_sigma_narrow = 2.;
246 constexpr double tot_mean_wide = 15.;
247 constexpr double tot_sigma_wide = 4.5;
248
249 double thetot = 0.;
250
251 if (CLHEP::RandFlat::shoot(rndmEngine)<0.75) {
252 thetot = CLHEP::RandGaussZiggurat::shoot(rndmEngine, tot_mean_narrow, tot_sigma_narrow);
253 } else {
254 thetot = CLHEP::RandGaussZiggurat::shoot(rndmEngine, tot_mean_wide, tot_sigma_wide);
255 }
256
257 return std::max(thetot, 0.);
258 }
259
260}
#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 * at(size_type n) const
Access an element, as an rvalue.
value_type push_back(value_type pElem)
Add an element to the end of the collection.
size_type size() const noexcept
Returns the number of elements in 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
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 doubletPhi(const Identifier &id) const
int doubletZ(const Identifier &id) const
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 std::string & stName(StIndex index)
convert StIndex into a string
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.