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