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