ATLAS Offline Software
Loading...
Searching...
No Matches
MDTSimulation.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "MDTSimulation.h"
9#include <cmath>
10#include <vector>
11
12
13namespace L0Muon {
14
16 ATH_MSG_DEBUG("Initializing " << name() << "...");
17 ATH_CHECK(m_mdtDriftCircleKey.initialize());
18 ATH_CHECK(m_barrelCandidateKey.initialize());
19 ATH_CHECK(m_regionSelector.retrieve());
20 ATH_CHECK(m_idHelperSvc.retrieve());
21 ATH_CHECK(m_geoCtxKey.initialize());
22 ATH_CHECK(m_csfSegmentFinder.retrieve());
24 ATH_CHECK(m_ptEstimationTool.retrieve());
25
26 return StatusCode::SUCCESS;
27 }
28
29 StatusCode MDTSimulation::execute(const EventContext& ctx) const {
30
31 const xAOD::RPCCandDataContainer* barrelCandidates{};
32 ATH_CHECK(SG::get(barrelCandidates, m_barrelCandidateKey, ctx));
33
34
35 const ActsTrk::GeometryContext* geoCtx{nullptr};
36 ATH_CHECK(SG::get(geoCtx, m_geoCtxKey, ctx));
37
38 const ActsTrk::GeometryContext& gctx = *geoCtx;
39 std::vector<float> z_positions, r_positions;
40
41
42
43 for(const auto& cand : *barrelCandidates) {
44
45 float m=0, b=0;
46
47 if (!fitRPC(*cand, m, b, z_positions, r_positions)) {
48 ATH_MSG_DEBUG("RPC fit failed for this candidate");
49 continue; // do NOT abort event
50 }
51
52 std::vector<const xAOD::MdtDriftCircle*> mdtHits;
53 float eta = (static_cast<float>(cand->eta()) / static_cast<float>(xAOD::ICandData_v1::etaBitRange())) * (2.0f * xAOD::ICandData_v1::etaRange()) - xAOD::ICandData_v1::etaRange();
54 float phi = (static_cast<float>(cand->phi()) / static_cast<float>(xAOD::ICandData_v1::phiBitRange())) * xAOD::ICandData_v1::phiRange() - M_PI;
55 ATH_CHECK(collectMDTHits(ctx, gctx, eta, phi, mdtHits, m, b));
56
57 // Split hits by station
58 std::vector<const xAOD::MdtDriftCircle*> biHits;
59 std::vector<const xAOD::MdtDriftCircle*> bmHits;
60 std::vector<const xAOD::MdtDriftCircle*> boHits;
61
62
63 for (const xAOD::MdtDriftCircle* dc : mdtHits) {
64 if (!dc) continue;
65
66 const Identifier id = dc->identify();
67 const auto& idh = m_idHelperSvc->mdtIdHelper();
68 const std::string st = idh.stationNameString(idh.stationName(id));
69
70 if (st.rfind("BI", 0) == 0) {
71 biHits.push_back(dc);
72 } else if (st.rfind("BM", 0) == 0) {
73 bmHits.push_back(dc);
74 } else if (st.rfind("BO", 0) == 0) {
75 boHits.push_back(dc);
76 }
77 }
78
79
80 // Require at least two stations with enough hits
81 int nStationsWithEnoughHits = 0;
82 if (biHits.size() >= 3) ++nStationsWithEnoughHits;
83 if (bmHits.size() >= 3) ++nStationsWithEnoughHits;
84 if (boHits.size() >= 3) ++nStationsWithEnoughHits;
85
86 if (nStationsWithEnoughHits < 2) {
87 ATH_MSG_DEBUG("Bad candidate: fewer than 2 stations with enough MDT hits");
88 continue;
89 } else {
90 ATH_MSG_DEBUG("Good candidate");
91 }
92
93 // Run the segment finder independently for each station
94 std::vector<L0MDT::Segment> biSegmentsCSF;
95 std::vector<L0MDT::Segment> bmSegmentsCSF;
96 std::vector<L0MDT::Segment> boSegmentsCSF;
97
98 std::vector<L0MDT::Segment> biSegmentsLEG;
99 std::vector<L0MDT::Segment> bmSegmentsLEG;
100 std::vector<L0MDT::Segment> boSegmentsLEG;
101
102
103 if (biHits.size() >= 2) {
104 ATH_CHECK(m_csfSegmentFinder->findSegments(biHits, gctx, m, b, biSegmentsCSF));
105 ATH_CHECK(m_legendreSegmentFinder->findSegments(biHits, gctx, m, b, biSegmentsLEG));
106 }
107 if (bmHits.size() >= 2) {
108 ATH_CHECK(m_csfSegmentFinder->findSegments(bmHits, gctx, m, b, bmSegmentsCSF));
109 ATH_CHECK(m_legendreSegmentFinder->findSegments(bmHits, gctx, m, b, bmSegmentsLEG));
110 }
111 if (boHits.size() >= 2) {
112 ATH_CHECK(m_csfSegmentFinder->findSegments(boHits, gctx, m, b, boSegmentsCSF));
113 ATH_CHECK(m_legendreSegmentFinder->findSegments(boHits, gctx, m, b, boSegmentsLEG));
114 }
115
116
117 ATH_MSG_DEBUG("CSF segment summary: "
118 << " BI=" << biSegmentsCSF.size()
119 << " BM=" << bmSegmentsCSF.size()
120 << " BO=" << boSegmentsCSF.size());
121
122
123 ATH_MSG_DEBUG("LEG segment summary: "
124 << " BI=" << biSegmentsLEG.size()
125 << " BM=" << bmSegmentsLEG.size()
126 << " BO=" << boSegmentsLEG.size());
127
128 const L0MDT::Segment* biSeg = biSegmentsCSF.empty() ? nullptr : &biSegmentsCSF.front();
129 const L0MDT::Segment* bmSeg = bmSegmentsCSF.empty() ? nullptr : &bmSegmentsCSF.front();
130 const L0MDT::Segment* boSeg = boSegmentsCSF.empty() ? nullptr : &boSegmentsCSF.front();
131
132 const auto ptResult = m_ptEstimationTool->estimatePt(biSeg, bmSeg, boSeg);
133
134 if (ptResult) {
135 ATH_MSG_DEBUG("pT estimate | "
136 << "nStations=" << ptResult->nStations
137 << " pt=" << ptResult->pt
138 << " deltaBeta=" << ptResult->deltaBeta
139 << " sagitta=" << ptResult->sagitta
140 << " leverArm=" << ptResult->leverArm);
141 } else {
142 ATH_MSG_DEBUG("pT estimate not available for this candidate");
143 }
144
145 }
146
147 return StatusCode::SUCCESS;
148 }
149
150
151 bool MDTSimulation::fitRPC(const xAOD::RPCCandData& cand,float& m, float& b, std::vector<float>& z_positions, std::vector<float>& r_positions) const {
152
153 float eta = (static_cast<float>(cand.eta()) / static_cast<float>(xAOD::ICandData_v1::etaBitRange())) * (2.0f * xAOD::ICandData_v1::etaRange()) - xAOD::ICandData_v1::etaRange();
154 float theta = 2.f * std::atan(std::exp(-eta));
155 float tanTheta = std::tan(theta);
156
157 z_positions.clear();
158 r_positions.clear();
159
160 for (int i = 0; i < 4; ++i) {
161 const float z_pos = static_cast<float>(cand.zPos().at(i)) / static_cast<float>(xAOD::RPCCandData_v1::zPosBitRange()) * (2.0f * xAOD::RPCCandData_v1::zPosRange()) - xAOD::RPCCandData_v1::zPosRange();
162 z_positions.push_back(z_pos);
163 r_positions.push_back(z_pos * tanTheta);
164 }
165
166 size_t N = z_positions.size();
167 if (N < 1) return false; // nothing to fit
168
169 float sumZ=0, sumR=0, sumZZ=0, sumZR=0;
170 for (size_t i=0;i<N;++i) {
171 sumZ += z_positions[i];
172 sumR += r_positions[i];
173 sumZZ += z_positions[i]*z_positions[i];
174 sumZR += z_positions[i]*r_positions[i];
175 }
176
177 if (N>=2) {
178 float den = N*sumZZ - sumZ*sumZ;
179 if (den != 0) {
180 m = (N*sumZR - sumZ*sumR) / den;
181 b = (sumR - m*sumZ) / N;
182 }
183 } else {//if the RPC candidate has only one z position we provide the fit using IP
184 m = tanTheta;
185 b = 0.f;
186 }
187
188 return true;
189 }
190
191
192
193
194 StatusCode MDTSimulation::collectMDTHits(const EventContext& ctx, const ActsTrk::GeometryContext& gctx, float eta, float phi, std::vector<const xAOD::MdtDriftCircle*>& hits, float m, float b) const {
195
196
197 hits.clear();
198
199 const float dEta = 0.01f, dPhi = 0.01f;
200
201
202 RoiDescriptor roi(eta, eta-dEta, eta+dEta,
203 phi, phi-dPhi, phi+dPhi);
204 TrigRoiDescriptor trigROI(roi);
205
206
207 auto regSel = m_regionSelector->lookup(ctx);
208 if (!regSel) return StatusCode::FAILURE;
209
210 std::vector<IdentifierHash> hashList;
211 regSel->HashIDList(trigROI, hashList);
212
213
214 const xAOD::MdtDriftCircleContainer* driftCircles{};
215 ATH_CHECK(SG::get(driftCircles, m_mdtDriftCircleKey, ctx));
216
217 float windowSize = 5;
218
219 ATH_MSG_DEBUG("found n chamber hashes " << hashList.size());
221 IdContext modContext = m_idHelperSvc->mdtIdHelper().module_context();
222 for (const IdentifierHash& h : hashList) {
223 Identifier mId;
224 m_idHelperSvc->mdtIdHelper().get_id(h, mId, &modContext);
225 ATH_MSG_DEBUG("looking at chamber " << m_idHelperSvc->toString(mId));
226
227
228 if(!viewer.loadView(mId)){
229 continue;
230 }
231 for(const xAOD::MdtDriftCircle* dc : viewer){
232 const MuonGMR4::MdtReadoutElement* detEl = dc->readoutElement();
233 const float pitch = detEl->tubePitch();
234 const Amg::Transform3D& locToGlob= detEl->localToGlobalTransform(gctx,dc->measurementHash());
235 const Amg::Vector3D gpos = locToGlob* dc->localMeasurementPos() ;
236 float resZ = computeResidual(gpos, m, b);
237 if (std::abs(resZ) > windowSize * pitch) continue;
238
239 hits.push_back(dc);
240
241
242
243 }
244 }
245 return StatusCode::SUCCESS;
246 }
247
248
249 float MDTSimulation::computeResidual(const Amg::Vector3D& gpos, float m, float b) const {
250 float z_pred = (m!=0.f ? (gpos.perp() - b)/m : 0.f);
251 return gpos.z() - z_pred;
252 }
253
254
255
256} // namespace L0Muon
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
Scalar theta() const
theta method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x)
Header file for AthHistogramAlgorithm.
This class saves the "context" of an expanded identifier (ExpandedIdentifier) for compact or hash ver...
Definition IdContext.h:26
This is a "hash" representation of an Identifier.
Class describing a reconstructed MDT segment used by the L0Muon trigger.
ToolHandle< L0MDT::IL0MDTSegmentFinderTool > m_legendreSegmentFinder
SG::ReadHandleKey< xAOD::RPCCandDataContainer > m_barrelCandidateKey
virtual StatusCode initialize() override
float computeResidual(const Amg::Vector3D &gpos, float m, float b) const
Compute the residual between a MDT hit global position and the fit line.
virtual StatusCode execute(const EventContext &ctx) const override
bool fitRPC(const xAOD::RPCCandData &cand, float &m, float &b, std::vector< float > &z_positions, std::vector< float > &r_positions) const
ToolHandle< IRegSelTool > m_regionSelector
StatusCode collectMDTHits(const EventContext &ctx, const ActsTrk::GeometryContext &gctx, float eta, float phi, std::vector< const xAOD::MdtDriftCircle * > &hits, float m, float b) const
Collect MDT hits within a RoI around (eta, phi), keeping only those whose residual from the RPC fit l...
SG::ReadHandleKey< ActsTrk::GeometryContext > m_geoCtxKey
ToolHandle< L0MDT::IL0MDTSegmentFinderTool > m_csfSegmentFinder
SG::ReadHandleKey< xAOD::MdtDriftCircleContainer > m_mdtDriftCircleKey
ToolHandle< L0MDT::IPtEstimationTool > m_ptEstimationTool
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
Readout element to describe the Monitored Drift Tube (Mdt) chambers Mdt chambers usually comrpise out...
double tubePitch() const
Returns the pitch between 2 tubes in a layer.
const Amg::Transform3D & localToGlobalTransform(const ActsTrk::GeometryContext &ctx) const
Returns the transformation from the local coordinate system of the readout element into the global AT...
Describes the Region of Ineterest geometry It has basically 9 parameters.
nope - should be used for standalone also, perhaps need to protect the class def bits ifndef XAOD_ANA...
bool loadView(const Identifier &chamberId)
Loads the view matching the parsed identifier.
uint16_t eta() const
Retrieve the eta.
static constexpr uint16_t phiBitRange()
static constexpr float etaRange()
static constexpr float phiRange()
static constexpr uint16_t etaBitRange()
static constexpr uint16_t zPosBitRange()
static constexpr float zPosRange()
std::array< uint16_t, 4 > zPos() const
Retrieve the global z positions of the RPC sector logic.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
@ Chamber
View ends if the moduleHash changes.
MdtDriftCircle_v1 MdtDriftCircle
MdtDriftCircleContainer_v1 MdtDriftCircleContainer