ATLAS Offline Software
Loading...
Searching...
No Matches
MSVertexRecoTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "MSVertexRecoTool.h"
6
10
11#include "CLHEP/Random/RandFlat.h"
12#include "TMath.h"
13
14
15/*
16 MS vertex reconstruction routine
17 See documentation at https://cds.cern.ch/record/1455664 and https://cds.cern.ch/record/1520894
18 Takes Tracklets as input and creates vertices in the MS volume
19*/
20
21namespace {
22 constexpr unsigned int MAXPLANES = 100;
23}
24
25namespace Muon {
26
27 //** ----------------------------------------------------------------------------------------------------------------- **//
28
29 MSVertexRecoTool::MSVertexRecoTool(const std::string& type, const std::string& name, const IInterface* parent) :
30 AthAlgTool(type, name, parent) {
31 declareInterface<IMSVertexRecoTool>(this);
32 }
33
34 //** ----------------------------------------------------------------------------------------------------------------- **//
35
37 ATH_CHECK(m_idHelperSvc.retrieve());
38 ATH_CHECK(m_rndmSvc.retrieve());
39
40 ATH_CHECK(m_extrapolator.retrieve());
41 ATH_CHECK(m_xAODContainerKey.initialize());
42 ATH_CHECK(m_rpcTESKey.initialize());
43 ATH_CHECK(m_tgcTESKey.initialize());
44 ATH_CHECK(m_mdtTESKey.initialize());
45
46 for (const std::string& str : m_accMDT_str) m_nMDT_accs.push_back(SG::AuxElement::Accessor<int>(str));
47 for (const std::string& str : m_accRPC_str) m_nRPC_accs.push_back(SG::AuxElement::Accessor<int>(str));
48 for (const std::string& str : m_accTGC_str) m_nTGC_accs.push_back(SG::AuxElement::Accessor<int>(str));
49
50 return StatusCode::SUCCESS;
51 }
52
53 //** ----------------------------------------------------------------------------------------------------------------- **//
54
55 StatusCode MSVertexRecoTool::findMSvertices(const std::vector<Tracklet>& tracklets, std::vector<std::unique_ptr<MSVertex>>& vertices,
56 const EventContext& ctx) const {
57 ATHRNG::RNGWrapper* rngWrapper = m_rndmSvc->getEngine(this);
58 rngWrapper->setSeed(name(), ctx);
59 CLHEP::HepRandomEngine* rndmEngine = rngWrapper->getEngine(ctx);
60
62 ATH_CHECK(xAODVxContainer.record(std::make_unique<xAOD::VertexContainer>(), std::make_unique<xAOD::VertexAuxContainer>()));
63
64 if (tracklets.size() < 3) {
65 ATH_MSG_DEBUG("Fewer than 3 tracks found, vertexing not possible. Exiting...");
66 return StatusCode::SUCCESS;
67 }
68
69 if (tracklets.size() > m_maxGlobalTracklets) {
70 ATH_MSG_DEBUG("Too many tracklets found globally. Exiting...");
71 return StatusCode::SUCCESS;
72 }
73
74 // group the tracks
75 std::vector<Tracklet> BarrelTracklets;
76 std::vector<Tracklet> EndcapTracklets;
77 for (const Tracklet &tracklet : tracklets){
78 if (m_idHelperSvc->mdtIdHelper().isBarrel(tracklet.muonIdentifier()))
79 BarrelTracklets.push_back(tracklet);
80 else if (m_idHelperSvc->mdtIdHelper().isEndcap(tracklet.muonIdentifier()))
81 EndcapTracklets.push_back(tracklet);
82 }
83
84 if (BarrelTracklets.size() > m_maxClusterTracklets || EndcapTracklets.size() > m_maxClusterTracklets) {
85 ATH_MSG_DEBUG("Too many tracklets found in barrel or endcap for clustering. Exiting...");
86 return StatusCode::SUCCESS;
87 }
88
89 ATH_MSG_DEBUG("Running on event with " << BarrelTracklets.size() << " barrel tracklets, " << EndcapTracklets.size()
90 << " endcap tracklets.");
91
92 // find any clusters of tracks & decide if tracks are from single muon
93 std::vector<Muon::MSVertexRecoTool::TrkCluster> BarrelClusters = findTrackClusters(BarrelTracklets);
94 std::vector<Muon::MSVertexRecoTool::TrkCluster> EndcapClusters = findTrackClusters(EndcapTracklets);
95
96 // if doSystematics, remove tracklets according to the tracklet reco uncertainty and rerun the cluster finder
97 if (m_doSystematics) {
98 std::vector<Tracklet> BarrelSystTracklets, EndcapSystTracklets;
99 for (const Tracklet &BarrelTracklet : BarrelTracklets) {
100 double prob = CLHEP::RandFlat::shoot(rndmEngine, 0, 1);
101 if (prob > m_BarrelTrackletUncert) BarrelSystTracklets.push_back(BarrelTracklet);
102 }
103 if (BarrelSystTracklets.size() >= 3) {
104 std::vector<Muon::MSVertexRecoTool::TrkCluster> BarrelSystClusters = findTrackClusters(BarrelSystTracklets);
105 for (Muon::MSVertexRecoTool::TrkCluster &BarrelSystCluster : BarrelSystClusters) {
106 BarrelSystCluster.isSystematic = true;
107 BarrelClusters.push_back(BarrelSystCluster);
108 }
109 }
110
111 for (const Tracklet &EndcapTracklet : EndcapTracklets) {
112 double prob = CLHEP::RandFlat::shoot(rndmEngine, 0, 1);
113 if (prob > m_EndcapTrackletUncert) EndcapSystTracklets.push_back(EndcapTracklet);
114 }
115 if (EndcapSystTracklets.size() >= 3) {
116 std::vector<Muon::MSVertexRecoTool::TrkCluster> EndcapSystClusters = findTrackClusters(EndcapSystTracklets);
117 for (Muon::MSVertexRecoTool::TrkCluster &EndcapSystCluster : EndcapSystClusters) {
118 EndcapSystCluster.isSystematic = true;
119 EndcapClusters.push_back(EndcapSystCluster);
120 }
121 }
122 }
123
125 // find vertices in the barrel MS (vertices using barrel tracklets)
126 for (const Muon::MSVertexRecoTool::TrkCluster &BarrelCluster : BarrelClusters) {
127 if (BarrelCluster.ntrks < 3) continue;
128 ATH_MSG_DEBUG("Attempting to build vertex from " << BarrelCluster.ntrks << " tracklets in the barrel");
129 std::unique_ptr<MSVertex> barvertex(nullptr);
130 MSVxFinder(BarrelCluster.tracks, barvertex, ctx);
131 if (!barvertex) continue;
132 // barrel minimum good vertex criteria
133 if (barvertex->getChi2Probability() > m_VxChi2ProbCUT) {
134 HitCounter(barvertex.get(), ctx);
135 if (barvertex->getNMDT() > m_MinMDTHits && (barvertex->getNRPC() + barvertex->getNTGC()) > m_MinTrigHits) {
136 ATH_MSG_DEBUG("Vertex found in the barrel with n_trk = " << barvertex->getNTracks() << " located at (eta,phi) = ("
137 << barvertex->getPosition().eta() << ", "
138 << barvertex->getPosition().phi() << ")");
139 if (BarrelCluster.isSystematic) barvertex->setAuthor(3);
140 vertices.push_back(std::move(barvertex));
141 }
142 }
143 }
144
145 // find vertices in the endcap MS (vertices using endcap tracklets)
146 for (const Muon::MSVertexRecoTool::TrkCluster &EndcapCluster : EndcapClusters) {
147 if (EndcapCluster.ntrks < 3) continue;
148 ATH_MSG_DEBUG("Attempting to build vertex from " << EndcapCluster.ntrks << " tracklets in the endcap");
149
150 std::unique_ptr<MSVertex> endvertex(nullptr);
152 MSStraightLineVx_oldMethod(EndcapCluster.tracks, endvertex, ctx);
153 else
154 MSStraightLineVx(EndcapCluster.tracks, endvertex, ctx);
155
156 if (!endvertex) continue;
157 // endcap minimum good vertex criteria
158 if (endvertex->getPosition().perp() < m_MaxLxyEndcap && std::abs(endvertex->getPosition().z()) < m_MaxZEndcap &&
159 std::abs(endvertex->getPosition().z()) > m_MinZEndcap && endvertex->getNTracks() >= 3) {
160 HitCounter(endvertex.get(), ctx);
161 if (endvertex->getNMDT() > m_MinMDTHits && (endvertex->getNRPC() + endvertex->getNTGC()) > m_MinTrigHits) {
162 ATH_MSG_DEBUG("Vertex found in the endcap with n_trk = " << endvertex->getNTracks() << " located at (eta,phi) = ("
163 << endvertex->getPosition().eta() << ", "
164 << endvertex->getPosition().phi() << ")");
165 if (EndcapCluster.isSystematic) endvertex->setAuthor(4);
166 vertices.push_back(std::move(endvertex));
167 }
168 }
169
170 } // end loop on endcap tracklet clusters
171
172 ATH_CHECK(FillOutputContainer(vertices, xAODVxContainer));
173 return StatusCode::SUCCESS;
174 } // end find vertices
175
176 //** ----------------------------------------------------------------------------------------------------------------- **//
177
178 std::optional<Muon::MSVertexRecoTool::TrkCluster> MSVertexRecoTool::ClusterizeTracks(std::vector<Tracklet>& tracks) const {
179 // returns the best cluster of tracklets and removes them from the input vector for the next iteration
180
181 if (tracks.size() > m_maxClusterTracklets) {
182 ATH_MSG_DEBUG("Too many tracks found, returning empty cluster");
183 return std::nullopt;
184 }
185
186 std::vector<TrkCluster> trkClu;
187 std::vector<TrkCluster> trkClu0;
188 // use each tracklet as a seed for the clusters
189 int ncluster = 0;
190 for (const Tracklet& trk : tracks) {
191 TrkCluster clu;
192 clu.eta = trk.globalPosition().eta();
193 clu.phi = trk.globalPosition().phi();
194
195 trkClu.push_back(clu);
196 trkClu0.push_back(clu);
197 ++ncluster;
198 if (ncluster >= 99) {
199 return std::nullopt;
200 }
201 }
202
203 // loop on the clusters and let the center move to find the optimal cluster centers
204 for (TrkCluster& clu : trkClu) {
205 // add tracklets to the cluster and update it
206 int ntracks = 0;
207 for (const TrkCluster& trk : trkClu0) {
208 double dEta = clu.eta - trk.eta;
209 double dPhi = xAOD::P4Helpers::deltaPhi(clu.phi , trk.phi);
210 if (std::abs(dEta) < m_ClusterdEta && std::abs(dPhi) < m_ClusterdPhi) {
211 ++ntracks;
212 clu.eta = clu.eta - dEta / ntracks;
213 clu.phi = xAOD::P4Helpers::deltaPhi(clu.phi - dPhi / ntracks, 0);
214 }
215 }
216
217 // the updated cluster position might cause tracklets considered near the start of the loop to now lie inside or outside the cluster
218 // run over all tracklets again to check is any more tracklets are picked up. Is so, do this at most 5 times
219 // *_best store the centre of the cluster containing the most tracklets
220 double eta_best = clu.eta;
221 double phi_best = clu.phi;
222 int nitr = 0;
223 bool improvement = true;
224 while (improvement) {
225 unsigned int ntracks_new = 0;
226 double eta_new = 0.0;
227 double phi_new = 0.0;
228 double cosPhi_new = 0.0;
229 double sinPhi_new = 0.0;
230
231 for (const TrkCluster& trk : trkClu0) {
232 double dEta = clu.eta - trk.eta;
233 double dPhi = xAOD::P4Helpers::deltaPhi(clu.phi , trk.phi);
234 if (std::abs(dEta) < m_ClusterdEta && std::abs(dPhi) < m_ClusterdPhi) {
235 eta_new += trk.eta;
236 cosPhi_new += std::cos(trk.phi);
237 sinPhi_new += std::sin(trk.phi);
238 ++ntracks_new;
239 }
240 }
241
242 eta_new = eta_new / ntracks_new;
243 phi_new = std::atan2(sinPhi_new / ntracks_new, cosPhi_new / ntracks_new);
244
245 if (ntracks_new > clu.ntrks) {
246 // better cluster found - update the centre and number of tracklets
247 eta_best = clu.eta; // not eta_new in case the iteration threshold was exceeded
248 phi_best = clu.phi;
249 clu.ntrks = ntracks_new;
250 if (nitr < 6) {
251 // update the cluster for the next improvement iteration
252 clu.eta = eta_new;
253 clu.phi = phi_new;
254 }
255 else
256 break;
257 }
258 else {
259 clu.eta = eta_best;
260 clu.phi = phi_best;
261 improvement = false;
262 }
263 ++nitr;
264 } // end while loop to check for cluster improvements
265 } // end loop over clusters
266
267 // find the best cluster as the one containing the most tracklets
268 TrkCluster& BestCluster = trkClu[0];
269 for (const TrkCluster& clu : trkClu) {
270 if (clu.ntrks > BestCluster.ntrks) BestCluster = clu;
271 }
272
273 // store the tracks inside the cluster
274 std::vector<Tracklet> unusedTracks;
275 for (const Tracklet& trk : tracks) {
276 double dEta = BestCluster.eta - trk.globalPosition().eta();
277 double dPhi = xAOD::P4Helpers::deltaPhi(BestCluster.phi , trk.globalPosition().phi());
278 if (std::abs(dEta) < m_ClusterdEta && std::abs(dPhi) < m_ClusterdPhi)
279 BestCluster.tracks.push_back(trk);
280 else
281 unusedTracks.push_back(trk);
282 }
283 // return the best cluster and the unused tracklets
284 tracks = std::move(unusedTracks);
285 return BestCluster;
286 }
287
288 //** ----------------------------------------------------------------------------------------------------------------- **//
289
290 std::vector<Muon::MSVertexRecoTool::TrkCluster> MSVertexRecoTool::findTrackClusters(const std::vector<Tracklet>& tracks) const {
291 // only clusters with 3 or more tracklets are returned
292 std::vector<Tracklet> trks = tracks;
293 std::vector<TrkCluster> clusters;
294 // keep making clusters until there are no more possible
295 while (true) {
296 if (trks.size() < 3) break;
297 std::optional<TrkCluster> clust = ClusterizeTracks(trks);
298 if (clust && clust->ntrks>=3) clusters.push_back(clust.value());
299 else break;
300 }
301
302 return clusters;
303 }
304
305 //** ----------------------------------------------------------------------------------------------------------------- **//
306
307 void MSVertexRecoTool::MSVxFinder(const std::vector<Tracklet>& tracklets, std::unique_ptr<MSVertex>& vtx,
308 const EventContext& ctx) const {
309 int nTrkToVertex(0);
310 double NominalAngle(m_TrackPhiAngle.value()), RotationAngle(m_TrackPhiAngle.value() + m_TrackPhiRotation.value());
311
312 Amg::Vector3D aveTrkPos(0, 0, 0);
313 for (const Tracklet &trk : tracklets) aveTrkPos += trk.globalPosition();
314 aveTrkPos /= tracklets.size();
315
316 // calculate the two angles (theta & phi)
317 double avePhi = aveTrkPos.phi();
318 double LoF = std::atan2(aveTrkPos.perp(), aveTrkPos.z()); // Line of Flight (theta)
319 avePhi = vxPhiFinder(std::abs(LoF), avePhi, ctx);
320
321 // find the positions of the radial planes
322 std::vector<double> Rpos;
324 double LoFdist = std::abs(RadialDist / std::sin(LoF));
325 int nplanes = LoFdist / m_VxPlaneDist + 1;
326 double PlaneSpacing = std::abs(m_VxPlaneDist / std::cos(LoF));
327 for (int k = 0; k < nplanes; ++k) Rpos.push_back(m_VertexMinRadialPlane + PlaneSpacing * k);
328
329 // loop on barrel tracklets and create two types of track parameters -- nominal and phi shifted tracklets
330 std::array< std::vector<std::unique_ptr<Trk::TrackParameters>>, MAXPLANES> TracksForVertexing{}; // vector of tracklets to be used at each vertex plane
331 std::array< std::vector<std::unique_ptr<Trk::TrackParameters>>, MAXPLANES> TracksForErrors{}; // vector of tracklets to be used for uncertainty at each vertex plane
332 std::array< std::vector<bool>, MAXPLANES> isNeutralTrack{};
333
334 for (const Tracklet &trk : tracklets) {
335 if (m_idHelperSvc->isEndcap(trk.muonIdentifier())) continue;
336 ++nTrkToVertex;
337 // coordinate transform variables
338 Amg::Vector3D trkgpos(trk.globalPosition().perp() * std::cos(avePhi),
339 trk.globalPosition().perp() * std::sin(avePhi),
340 trk.globalPosition().z());
341 double x0 = trkgpos.x();
342 double y0 = trkgpos.y();
343 double r0 = trkgpos.perp();
344
345 // decide which way the tracklet gets rotated -- positive or negative phi
346 double anglesign = xAOD::P4Helpers::deltaPhi(trk.globalPosition().phi(), avePhi) < 0 ? -1.0 : 1.0;
347 double NominalTrkAng = anglesign * NominalAngle; // in case there is a nominal tracklet angle
348 double MaxTrkAng = anglesign * RotationAngle; // the rotated tracklet phi position
349
350 // loop over the radial planes
351 for (int k = 0; k < nplanes; ++k) {
352 // only use tracklets that start AFTER the vertex plane
353 if (Rpos[k] > trk.globalPosition().perp()) break;
354
355 // nominal tracks for vertexing
356 double Xp = Rpos[k] * std::cos(avePhi);
357 double Yp = Rpos[k] * std::sin(avePhi);
358 // in case there is a nominal opening angle, calculate tracklet direction
359 // the tracklet must cross the candidate vertex plane at the correct phi
360 double DelR = std::hypot(x0 - Xp, y0 - Yp) / std::cos(NominalAngle);
361 double X1 = DelR * std::cos(NominalTrkAng + avePhi) + Xp;
362 double Y1 = DelR * std::sin(NominalTrkAng + avePhi) + Yp;
363 double R1 = std::hypot(X1, Y1);
364 double Norm = r0 / R1;
365 X1 = X1 * Norm;
366 Y1 = Y1 * Norm;
367 double Dirmag = std::hypot(X1 - Xp, Y1 - Yp);
368 double Xdir = (X1 - Xp) / Dirmag;
369 double Ydir = (Y1 - Yp) / Dirmag;
370 double trkpx = Xdir * trk.momentum().perp();
371 double trkpy = Ydir * trk.momentum().perp();
372 double trkpz = trk.momentum().z();
373
374 // check if the tracklet has a charge & momentum measurement -- if not, set charge=1 so extrapolator will work
375 double charge = trk.charge();
376 if (std::abs(charge) < 0.1) {
377 charge = 1; // for "straight" tracks, set charge = 1
378 isNeutralTrack[k].push_back(true);
379 } else
380 isNeutralTrack[k].push_back(false);
381
382 // store the tracklet as a Trk::Perigee
383 Amg::Vector3D trkmomentum(trkpx, trkpy, trkpz);
384 Amg::Vector3D trkgpos(X1, Y1, trk.globalPosition().z());
385 AmgSymMatrix(5) covariance = AmgSymMatrix(5)(trk.errorMatrix());
386 TracksForVertexing[k].push_back(std::make_unique<Trk::Perigee>(0., 0., trkmomentum.phi(), trkmomentum.theta(), charge / trkmomentum.mag(), Trk::PerigeeSurface(trkgpos), covariance));
387
388 // tracks for errors -- rotate the plane & recalculate the tracklet parameters
389 double xp = Rpos[k] * std::cos(avePhi);
390 double yp = Rpos[k] * std::sin(avePhi);
391 double delR = std::hypot(x0 - xp, y0 - yp) / std::cos(RotationAngle);
392 double x1 = delR * std::cos(MaxTrkAng + avePhi) + xp;
393 double y1 = delR * std::sin(MaxTrkAng + avePhi) + yp;
394 double r1 = std::hypot(x1, y1);
395 double norm = r0 / r1;
396 x1 = x1 * norm;
397 y1 = y1 * norm;
398 double dirmag = std::hypot(x1 - xp, y1 - yp);
399 double xdir = (x1 - xp) / dirmag;
400 double ydir = (y1 - yp) / dirmag;
401 double errpx = xdir * trk.momentum().perp();
402 double errpy = ydir * trk.momentum().perp();
403 double errpz = trk.momentum().z();
404
405 // store the tracklet as a Trk::Perigee
406 AmgSymMatrix(5) covariance2 = AmgSymMatrix(5)(trk.errorMatrix());
407 Amg::Vector3D trkerrmom(errpx, errpy, errpz);
408 Amg::Vector3D trkerrpos(x1, y1, trk.globalPosition().z());
409 TracksForErrors[k].push_back(std::make_unique<Trk::Perigee>(0., 0., trkerrmom.phi(), trkerrmom.theta(), charge / trkerrmom.mag(), Trk::PerigeeSurface(trkerrpos), covariance2));
410 } // end loop on vertex planes
411 } // end loop on tracks
412
413 // return if there are not enough tracklets
414 if (nTrkToVertex < 3) return;
415
416 // calculate the tracklet positions and uncertainty on each surface
417 bool boundaryCheck = true;
418 std::array<std::vector<double>, MAXPLANES> ExtrapZ{}; // extrapolated z position
419 std::array<std::vector<double>, MAXPLANES> dlength{}; // extrapolated z position uncertainty
420 std::array<std::vector<std::pair<unsigned int, unsigned int>>, MAXPLANES> UsedTracks{};
421 std::array<std::vector<bool>, MAXPLANES> ExtrapSuc{}; // did the extrapolation succeed?
422 std::vector<std::unique_ptr<MSVertex>> vertices; vertices.reserve(nplanes);
423 std::array<std::vector<double>, MAXPLANES> sigmaZ{}; // total uncertainty at each plane
424 std::array<std::vector<Amg::Vector3D>, MAXPLANES> pAtVx{}; // tracklet momentum expressed at the plane
425
426 // extrapolate tracklates and store results at each radial plane
427 for (int k = 0; k < nplanes; ++k) { // loop on planes
428 double rpos = Rpos[k];
429 for (unsigned int i = 0; i < TracksForVertexing[k].size(); ++i) { // loop on tracklets
430 // at least three tracklets per plane are needed
431 if (TracksForVertexing[k].size() < 3) break;
432
433 Amg::Transform3D surfaceTransformMatrix;
434 surfaceTransformMatrix.setIdentity();
435 Trk::CylinderSurface cyl(surfaceTransformMatrix, rpos, 10000.); // create the surface
436 // extrapolate to the surface
437 std::unique_ptr<const Trk::TrackParameters> extrap_par(
438 m_extrapolator->extrapolate(ctx,
439 *TracksForVertexing[k].at(i), cyl, Trk::anyDirection, boundaryCheck, Trk::muon));
440
441 const Trk::AtaCylinder* extrap = dynamic_cast<const Trk::AtaCylinder*>(extrap_par.get());
442
443 if (extrap) {
444 // if the track is neutral just store the uncertainty due to angular uncertainty of the orignal tracklet
445 if (isNeutralTrack[k].at(i)) {
446 double pTot = std::hypot(TracksForVertexing[k].at(i)->momentum().perp(), TracksForVertexing[k].at(i)->momentum().z());
447 double dirErr = Amg::error(*TracksForVertexing[k].at(i)->covariance(), Trk::theta);
448 double extrapRdist = TracksForVertexing[k].at(i)->position().perp() - Rpos[k];
449 double sz = std::abs(20 * dirErr * extrapRdist * std::pow(pTot,2) / std::pow(TracksForVertexing[k].at(i)->momentum().perp(), 2));
450 double ExtrapErr = sz;
451 if (ExtrapErr > m_MaxTrackUncert)
452 ExtrapSuc[k].push_back(false);
453 else {
454 ExtrapSuc[k].push_back(true);
455 std::pair<unsigned int, unsigned int> trkmap(ExtrapZ[k].size(), i);
456 UsedTracks[k].push_back(trkmap);
457 ExtrapZ[k].push_back(extrap->localPosition().y());
458 sigmaZ[k].push_back(sz);
459 pAtVx[k].push_back(extrap->momentum());
460 dlength[k].push_back(0);
461 }
462 } // end neutral tracklets
463 // if the tracklet has a momentum measurement
464 else {
465 // now extrapolate taking into account the extra path length & differing magnetic field
466 Amg::Transform3D srfTransMat2;
467 srfTransMat2.setIdentity();
468 Trk::CylinderSurface cyl2(srfTransMat2, rpos, 10000.);
469 std::unique_ptr<const Trk::TrackParameters> extrap_par2(
470 m_extrapolator->extrapolate(ctx,
471 *TracksForErrors[k].at(i), cyl, Trk::anyDirection, boundaryCheck, Trk::muon));
472 const Trk::AtaCylinder* extrap2 = dynamic_cast<const Trk::AtaCylinder*>(extrap_par2.get());
473
474 if (extrap2) {
475 double sz = Amg::error(*extrap->covariance(), Trk::locY);
476 double zdiff = extrap->localPosition().y() - extrap2->localPosition().y();
477 double ExtrapErr = std::hypot(sz, zdiff);
478 if (ExtrapErr > m_MaxTrackUncert)
479 ExtrapSuc[k].push_back(false);
480 else {
481 // iff both extrapolations succeed && error is acceptable, store the information
482 ExtrapSuc[k].push_back(true);
483 std::pair<unsigned int, unsigned int> trkmap(ExtrapZ[k].size(), i);
484 UsedTracks[k].push_back(trkmap);
485 ExtrapZ[k].push_back(extrap->localPosition().y());
486 sigmaZ[k].push_back(sz);
487 pAtVx[k].push_back(extrap->momentum());
488 dlength[k].push_back(zdiff);
489 }
490 } else
491 ExtrapSuc[k].push_back(false); // not possible to calculate the uncertainty -- do not use tracklet in vertex
492 }
493 }
494 // not possible to extrapolate the tracklet
495 else
496 ExtrapSuc[k].push_back(false);
497 } // loop on tracklets
498 } // loop on radial planes
499
500
501 // perform the vertex fit
502 std::array<std::vector<Amg::Vector3D>, MAXPLANES> trkp{}; // tracklet momentum
503 // loop on planes
504 for (int k = 0; k < nplanes; ++k) {
505 if (ExtrapZ[k].size() < 3) continue; // require at least 3 tracklets to build a vertex
506 // initialize the variables used in the routine
507 double zLoF = Rpos[k] / std::tan(LoF);
508 double dzLoF(10);
509 double aveZpos(0), posWeight(0);
510 for (unsigned int i = 0; i < ExtrapZ[k].size(); ++i) {
511 double ExtrapErr = std::hypot(sigmaZ[k][i], dlength[k][i], dzLoF);
512 if (isNeutralTrack[k][i]) ExtrapErr = std::hypot(sigmaZ[k][i], dzLoF);
513 aveZpos += ExtrapZ[k][i] / std::pow(ExtrapErr,2);
514 posWeight += 1. / std::pow(ExtrapErr,2);
515 }
516 // calculate the weighted average position of the tracklets
517 zLoF = aveZpos / posWeight;
518 double zpossigma(dzLoF), Chi2(0), Chi2Prob(-1);
519 unsigned int Nitr(0);
520 std::vector<unsigned int> vxtracks; // tracklets to be used in the vertex routine
521 std::vector<bool> blocklist(ExtrapZ[k].size(), false); // tracklets that do not belong to the vertex
522
523 // minimum chi^2 iterative fit
524 while (true) {
525 vxtracks.clear(); trkp[k].clear();
526 int tmpnTrks(0);
527 double tmpzLoF(0), tmpzpossigma(0), tmpchi2(0), posWeight(0), worstdelz(0);
528 unsigned int iworst(0); // tracklet index contributing to the vertex chi2 the most
529 // loop on the tracklets, find the chi^2 contribution from each tracklet
530 for (unsigned int i = 0; i < ExtrapZ[k].size(); ++i) {
531 if (blocklist[i]) continue;
532 trkp[k].push_back(pAtVx[k][i]);
533 double delz = zLoF - ExtrapZ[k][i];
534 double ExtrapErr = std::hypot(sigmaZ[k][i], dlength[k][i], dzLoF);
535 double trkchi2 = std::pow(delz,2) / std::pow(ExtrapErr,2);
536 if (trkchi2 > worstdelz) {
537 iworst = i;
538 worstdelz = trkchi2;
539 }
540 tmpzLoF += ExtrapZ[k][i] / std::pow(ExtrapErr,2);
541 posWeight += 1. / std::pow(ExtrapErr,2);
542 tmpzpossigma += std::pow(delz,2);
543 tmpchi2 += trkchi2;
544 ++tmpnTrks;
545 }
546
547 if (tmpnTrks < 3) break; // stop searching for a vertex at this plane
548 tmpzpossigma = std::sqrt(tmpzpossigma / (double)tmpnTrks);
549 zLoF = tmpzLoF / posWeight;
550 zpossigma = tmpzpossigma;
551 double testChi2 = TMath::Prob(tmpchi2, tmpnTrks - 1);
552 if (testChi2 < m_VxChi2ProbCUT)
553 blocklist[iworst] = true;
554 else {
555 Chi2 = tmpchi2;
556 Chi2Prob = testChi2;
557 // loop on the tracklets and find all that belong to the vertex
558 for (unsigned int i = 0; i < ExtrapZ[k].size(); ++i) {
559 double delz = zLoF - ExtrapZ[k][i];
560 double ExtrapErr = std::hypot(sigmaZ[k][i], dlength[k][i], dzLoF);
561 double trkErr = std::hypot(ExtrapErr, zpossigma) + 0.001;
562 double trkNsigma = std::abs(delz / trkErr);
563 if (trkNsigma < 3) vxtracks.push_back(i);
564 }
565 break; // found a vertex, stop removing tracklets! Break chi^2 iterative fit at this radial plane
566 }
567 if (Nitr >= (ExtrapZ[k].size() - 3)) break; // stop searching for a vertex at this plane
568 ++Nitr;
569 } // end while
570
571 if (vxtracks.size() < 3) continue;
572
573 // create TrackParticle vector for all tracklets used in the vertex fit
574 std::vector<const xAOD::TrackParticle*> vxTrackParticles;
575 vxTrackParticles.reserve(vxtracks.size());
576 for (std::vector<unsigned int>::iterator vxtrk = vxtracks.begin(); vxtrk != vxtracks.end(); ++vxtrk) {
577 for (unsigned int i = 0; i < UsedTracks[k].size(); ++i) {
578 if ((*vxtrk) != UsedTracks[k].at(i).first) continue;
579 const Tracklet& trklt = tracklets.at(UsedTracks[k].at(i).second);
580 vxTrackParticles.push_back(trklt.getTrackParticle());
581 break; // found the tracklet used for the vertex reconstruction in the tracklet collection. Hence can stop looking
582 }
583 }
584 Amg::Vector3D position(Rpos[k] * std::cos(avePhi), Rpos[k] * std::sin(avePhi), zLoF);
585 vertices.push_back(std::make_unique<MSVertex>(1, position, vxTrackParticles, Chi2Prob, Chi2, 0, 0, 0));
586 } // end loop on Radial planes
587
588 if (vertices.empty()) return;
589
590 // loop on the vertex candidates and select the best based on max n(tracks) and max chi^2 probability
591
592 unsigned int bestVx(0);
593 for (unsigned int k = 1; k < vertices.size(); ++k) {
594 if (vertices[k]->getChi2Probability() < m_VxChi2ProbCUT || vertices[k]->getNTracks() < 3) continue;
595 if (vertices[k]->getNTracks() < vertices[bestVx]->getNTracks()) continue;
596 if (vertices[k]->getNTracks() == vertices[bestVx]->getNTracks() &&
597 vertices[k]->getChi2Probability() < vertices[bestVx]->getChi2Probability())
598 continue;
599 bestVx = k;
600 }
601 vtx = std::make_unique<MSVertex>(*vertices[bestVx]);
602 vertices.clear(); // cleanup
603 }
604
605 //** ----------------------------------------------------------------------------------------------------------------- **//
606
607 void MSVertexRecoTool::MSStraightLineVx(const std::vector<Tracklet>& trks, std::unique_ptr<MSVertex>& vtx,
608 const EventContext& ctx) const {
609 // Running set of all vertices found. The inner set is the indices of trks that are used to make the vertex
610 std::set<std::set<int> > prelim_vx;
611
612 // We don't consider all 3-tracklet combinations when a high number of tracklets is found
613 // Faster method is used for > 40 tracklets
614 if (trks.size() > 40) {
615 MSStraightLineVx_oldMethod(trks, vtx, ctx);
616 return;
617 }
618
619 // Okay, if we get here then we know there's 40 or fewer tracklets in the cluster.
620 // Make a list of all 3-tracklet combinations that make vertices
621 for (unsigned int i = 0; i < trks.size() - 2; ++i) {
622 for (unsigned int j = i + 1; j < trks.size() - 1; ++j) {
623 for (unsigned int k = j + 1; k < trks.size(); ++k) {
624 std::set<int> tmpTracks;
625 tmpTracks.insert(i);
626 tmpTracks.insert(j);
627 tmpTracks.insert(k);
628
629 Amg::Vector3D MyVx;
630 MyVx = VxMinQuad(getTracklets(trks, tmpTracks));
631 if (MyVx.perp() < 10000 && std::abs(MyVx.z()) > 7000 && std::abs(MyVx.z()) < 15000 &&
632 !EndcapHasBadTrack(getTracklets(trks, tmpTracks), MyVx))
633 prelim_vx.insert(tmpTracks);
634 }
635 }
636 }
637
638 // If no preliminary vertices were found from 3 tracklets, then there is no vertex and we are done.
639 if (prelim_vx.empty()) return;
640
641 // The remaining algorithm is very time consuming for large numbers of tracklets. To control this,
642 // we run the old algorithm when there are too many tracklets and a vertex is found.
643 if (trks.size() <= 20) {
644 std::set<std::set<int> > new_prelim_vx = prelim_vx;
645 std::set<std::set<int> > old_prelim_vx;
646
647 int foundNewVx = true;
648 while (foundNewVx) {
649 foundNewVx = false;
650
651 old_prelim_vx = new_prelim_vx;
652 new_prelim_vx.clear();
653
654 for (std::set<std::set<int> >::iterator itr = old_prelim_vx.begin(); itr != old_prelim_vx.end(); ++itr) {
655 for (unsigned int i_trks = 0; i_trks < trks.size(); ++i_trks) {
656 std::set<int> tempCluster = *itr;
657 if (tempCluster.insert(i_trks).second) {
658 Amg::Vector3D MyVx = VxMinQuad(getTracklets(trks, tempCluster));
659 if (MyVx.perp() < 10000 && std::abs(MyVx.z()) > 7000 && std::abs(MyVx.z()) < 15000 &&
660 !EndcapHasBadTrack(getTracklets(trks, tempCluster), MyVx)) {
661 new_prelim_vx.insert(tempCluster);
662 prelim_vx.insert(tempCluster);
663 foundNewVx = true;
664 }
665 }
666 }
667 }
668 }
669 } else {
670 // Since there are 20 or more tracklets, we're going to use the old MSVx finding method. Note that
671 // if the old method fails, we do not return here; in this case a 3-tracklet vertex that was found
672 // earlier in this algorithm will be returned
673 MSStraightLineVx_oldMethod(trks, vtx, ctx);
674 if (vtx) return;
675 }
676
677 // Find the preliminary vertex with the maximum number of tracklets - that is the final vertex. If
678 // multiple preliminary vertices with same number of tracklets, the first one found is returned
679 std::set<std::set<int> >::iterator prelim_vx_max = prelim_vx.begin();
680 for (std::set<std::set<int> >::iterator itr = prelim_vx.begin(); itr != prelim_vx.end(); ++itr) {
681 if ((*itr).size() > (*prelim_vx_max).size()) prelim_vx_max = itr;
682 }
683
684 std::vector<Tracklet> tracklets = getTracklets(trks, *prelim_vx_max);
685 // use tracklets to estimate the line of flight of decaying particle
686 double aveX(0);
687 double aveY(0);
688 for (const Tracklet &trk : tracklets) {
689 aveX += trk.globalPosition().x();
690 aveY += trk.globalPosition().y();
691 }
692
693 Amg::Vector3D MyVx = VxMinQuad(tracklets);
694 double vxtheta = std::atan2(MyVx.x(), MyVx.z());
695 double tracklet_vxphi = std::atan2(aveY, aveX);
696 double vxphi = vxPhiFinder(std::abs(vxtheta), tracklet_vxphi, ctx);
697
698 Amg::Vector3D vxpos(MyVx.x() * std::cos(vxphi), MyVx.x() * std::sin(vxphi), MyVx.z());
699
700 std::vector<const xAOD::TrackParticle*> vxTrackParticles;
701 for (const Tracklet &trk : tracklets) vxTrackParticles.push_back(trk.getTrackParticle());
702
703 vtx = std::make_unique<MSVertex>(2, vxpos, vxTrackParticles, 1, vxTrackParticles.size(), 0, 0, 0);
704 }
705
706 //** ----------------------------------------------------------------------------------------------------------------- **//
707
708 // vertex finding routine for the endcap
709 void MSVertexRecoTool::MSStraightLineVx_oldMethod(const std::vector<Tracklet>& trks, std::unique_ptr<MSVertex>& vtx,
710 const EventContext& ctx) const {
711 // find the line of flight
712 double aveX(0), aveY(0);
713 for (const Tracklet &trk : trks) {
714 aveX += trk.globalPosition().x();
715 aveY += trk.globalPosition().y();
716 }
717 double vxphi = std::atan2(aveY, aveX);
718
719 Amg::Vector3D MyVx(0, 0, 0);
720 std::vector<Tracklet> tracks = RemoveBadTrk(trks, MyVx);
721 if (tracks.size() < 2) return;
722
723 // remove back tracks one by one until non are considered bad (large distance from the vertex)
724 while (true) {
725 MyVx = VxMinQuad(tracks);
726 std::vector<Tracklet> Tracks = RemoveBadTrk(tracks, MyVx);
727 if (tracks.size() == Tracks.size()) break;
728 tracks = std::move(Tracks);
729 }
730
731 if (tracks.size() >= 3 && MyVx.x() > 0) {
732 double vxtheta = std::atan2(MyVx.x(), MyVx.z());
733 vxphi = vxPhiFinder(std::abs(vxtheta), vxphi, ctx);
734 Amg::Vector3D vxpos(MyVx.x() * std::cos(vxphi), MyVx.x() * std::sin(vxphi), MyVx.z());
735
736 std::vector<const xAOD::TrackParticle*> vxTrackParticles;
737 for (const Tracklet &trk : tracks) vxTrackParticles.push_back(trk.getTrackParticle());
738
739 vtx = std::make_unique<MSVertex>(2, vxpos, vxTrackParticles, 1, (double)vxTrackParticles.size(), 0, 0, 0);
740 }
741 }
742
743 //** ----------------------------------------------------------------------------------------------------------------- **//
744
745 std::vector<Tracklet> MSVertexRecoTool::RemoveBadTrk(const std::vector<Tracklet>& tracks, const Amg::Vector3D& Vx) const {
746 // Removes at most one track with the largest distance to the vertex above the predefined threshold set by m_MaxTollDist
747 // check for default vertex
748 if (Vx.x() == 0 && Vx.z() == 0) return tracks;
749
750 double WorstTrkDist = m_MaxTollDist;
751 unsigned int iWorstTrk = -1;
752 for (unsigned int i = 0; i < tracks.size(); ++i) {
753 double TrkSlope = std::tan(tracks.at(i).getML1seg().alpha());
754 double TrkInter = tracks.at(i).getML1seg().globalPosition().perp() - tracks.at(i).getML1seg().globalPosition().z() * TrkSlope;
755 double dist = std::abs((TrkSlope * Vx.z() - Vx.x() + TrkInter) / std::hypot(TrkSlope, 1));
756 if (dist > m_MaxTollDist && dist > WorstTrkDist) {
757 iWorstTrk = i;
758 WorstTrkDist = dist;
759 }
760 }
761
762 // Remove the worst track from the list
763 std::vector<Tracklet> Tracks;
764 for (unsigned int i = 0; i < tracks.size(); ++i) {
765 if (i != iWorstTrk) Tracks.push_back(tracks.at(i));
766 }
767 return Tracks;
768 }
769
770 std::vector<Tracklet> MSVertexRecoTool::getTracklets(const std::vector<Tracklet>& trks, const std::set<int>& tracklet_subset) const {
771 std::vector<Tracklet> returnVal;
772 for (std::set<int>::const_iterator itr = tracklet_subset.cbegin(); itr != tracklet_subset.cend(); ++itr) {
773 if ((unsigned int)*itr > trks.size()) ATH_MSG_ERROR("ERROR - Index out of bounds in getTracklets");
774 returnVal.push_back(trks.at(*itr));
775 }
776
777 return returnVal;
778 }
779
780 //** ----------------------------------------------------------------------------------------------------------------- **//
781
782 bool MSVertexRecoTool::EndcapHasBadTrack(const std::vector<Tracklet>& tracks, const Amg::Vector3D& Vx) const {
783 if (Vx.x() == 0 && Vx.z() == 0) return true;
784 // return true a track is further away from the vertex than m_MaxTollDist
785 for (const Tracklet &track : tracks) {
786 double TrkSlope = std::tan(track.getML1seg().alpha());
787 double TrkInter = track.getML1seg().globalPosition().perp() - track.getML1seg().globalPosition().z() * TrkSlope;
788 double dist = std::abs((TrkSlope * Vx.z() - Vx.x() + TrkInter) / std::hypot(TrkSlope, 1));
789 if (dist > m_MaxTollDist) { return true; }
790 }
791
792 // No tracks found that are too far, so it is okay.
793 return false;
794 }
795
796 //** ----------------------------------------------------------------------------------------------------------------- **//
797
798 void MSVertexRecoTool::dressVtxHits(xAOD::Vertex* xAODVx, const std::vector<SG::AuxElement::Accessor<int>>& accs, const std::vector<int>& hits) const {
799 unsigned int i{0};
800 for (const SG::AuxElement::Accessor<int> &acc : accs) {
801 acc(*xAODVx) = hits[i];
802 ++i;
803 }
804
805 return;
806 }
807
808 //** ----------------------------------------------------------------------------------------------------------------- **//
809
810 StatusCode MSVertexRecoTool::FillOutputContainer(const std::vector<std::unique_ptr<MSVertex>>& vertices,
811 SG::WriteHandle<xAOD::VertexContainer>& xAODVxContainer) const {
812 for (const std::unique_ptr<MSVertex> &vtx : vertices){
813 xAOD::Vertex* xAODVx = new xAOD::Vertex();
814 xAODVx->makePrivateStore();
816 xAODVx->setPosition(vtx->getPosition());
817 xAODVx->setFitQuality(vtx->getChi2(), vtx->getNTracks() - 1);
818
819 // link TrackParticle to vertex
820 for (const xAOD::TrackParticle *trk : *(vtx->getTracks())) {
821 ElementLink<xAOD::TrackParticleContainer> link_trk(*(dynamic_cast<const xAOD::TrackParticleContainer *>(trk->container())), trk->index());
822 if (link_trk.isValid()) xAODVx->addTrackAtVertex(link_trk);
823 }
824
825 // store the new xAOD vertex
826 xAODVxContainer->push_back(xAODVx);
827
828 // dress the vertex with the hit counts
829 dressVtxHits(xAODVx, m_nMDT_accs, vtx->getNMDT_all());
830 dressVtxHits(xAODVx, m_nRPC_accs, vtx->getNRPC_all());
831 dressVtxHits(xAODVx, m_nTGC_accs, vtx->getNTGC_all());
832 }
833
834 return StatusCode::SUCCESS;
835 }
836
837 //** ----------------------------------------------------------------------------------------------------------------- **//
838
839 // core algorithm for endcap vertex reconstruction
840 Amg::Vector3D MSVertexRecoTool::VxMinQuad(const std::vector<Tracklet>& tracks) {
841 double s(0.), sx(0.), sy(0.), sxy(0.), sxx(0.), d(0.);
842 double sigma = 1.;
843 for (const Tracklet &track : tracks) {
844 double TrkSlope = std::tan(track.getML1seg().alpha());
845 double TrkInter = track.getML1seg().globalPosition().perp() - track.getML1seg().globalPosition().z() * TrkSlope;
846 s += 1. / std::pow(sigma,2);
847 sx += TrkSlope / std::pow(sigma,2);
848 sxx += std::pow(TrkSlope,2) / std::pow(sigma,2);
849 sy += TrkInter / std::pow(sigma,2);
850 sxy += (TrkSlope * TrkInter) / std::pow(sigma,2);
851 }
852 d = s * sxx - std::pow(sx,2);
853 if (d == 0.) {
854 Amg::Vector3D MyVx(0., 0., 0.); // return 0, no vertex was found.
855 return MyVx;
856 }
857
858 double Rpos = (sxx * sy - sx * sxy) / d;
859 double Zpos = (sx * sy - s * sxy) / d;
860
861 Amg::Vector3D MyVx(Rpos, 0, Zpos);
862
863 return MyVx;
864 }
865
866 //** ----------------------------------------------------------------------------------------------------------------- **//
867
868 // vertex phi location -- determined from the RPC/TGC hits
869 double MSVertexRecoTool::vxPhiFinder(const double theta, const double phi, const EventContext& ctx) const {
870 double nmeas(0), sinphi(0), cosphi(0);
871 if (theta == 0) {
872 ATH_MSG_WARNING("vxPhiFinder() called with theta=" << theta << " and phi=" << phi << ", return 0");
873 return 0;
874 } else if (theta > M_PI) {
875 ATH_MSG_WARNING("vxPhiFinder() called with theta=" << std::setprecision(15) << theta << " and phi=" << phi
876 << ", (theta>M_PI), return 0");
877 return 0;
878 }
879 double tanThetaHalf = std::tan(0.5 * theta);
880 if (tanThetaHalf <= 0) {
881 ATH_MSG_WARNING("vxPhiFinder() called with theta=" << std::setprecision(15) << theta << " and phi=" << phi
882 << ", resulting in tan(0.5*theta)<=0, return 0");
883 return 0;
884 }
885 double eta = -std::log(tanThetaHalf);
886 if (std::abs(eta) < 1.5) {
888 if (!rpcTES.isValid()) {
889 ATH_MSG_WARNING("No RpcPrepDataContainer found in SG!");
890 return 0;
891 }
892 for (const Muon::RpcPrepDataCollection* RPC_coll : *rpcTES){
893 for (const Muon::RpcPrepData* rpc : *RPC_coll){
894 if (!m_idHelperSvc->rpcIdHelper().measuresPhi(rpc->identify())) continue;
895 double rpcEta = rpc->globalPosition().eta();
896 double rpcPhi = rpc->globalPosition().phi();
897 double DR = xAOD::P4Helpers::deltaR(eta, phi, rpcEta, rpcPhi);
898 if (DR >= 0.6) continue;
899 sinphi += std::sin(rpcPhi);
900 cosphi += std::cos(rpcPhi);
901 ++nmeas;
902 }
903 }
904 }
905 if (std::abs(eta) > 0.5) {
907 if (!tgcTES.isValid()) {
908 ATH_MSG_WARNING("No TgcPrepDataContainer found in SG!");
909 return 0;
910 }
911 for (const Muon::TgcPrepDataCollection* TGC_coll : *tgcTES){
912 for (const Muon::TgcPrepData* tgc : *TGC_coll){
913 if (!m_idHelperSvc->tgcIdHelper().isStrip(tgc->identify())) continue;
914 double tgcEta = tgc->globalPosition().eta();
915 double tgcPhi = tgc->globalPosition().phi();
916 double DR = xAOD::P4Helpers::deltaR(eta, phi, tgcEta, tgcPhi);
917 if (DR >= 0.6) continue;
918 sinphi += std::sin(tgcPhi);
919 cosphi += std::cos(tgcPhi);
920 ++nmeas;
921 }
922 }
923 }
924
925 double vxphi = phi;
926 if (nmeas > 0) vxphi = std::atan2(sinphi / nmeas, cosphi / nmeas);
927 return vxphi;
928 }
929
930 //** ----------------------------------------------------------------------------------------------------------------- **//
931
932 void MSVertexRecoTool::HitCounter(MSVertex* MSRecoVx, const EventContext& ctx) const {
933 // count the hits (MDT, RPC & TGC) around the vertex and split them by layer
934 int nHighOccupancy(0);
935 int stationRegion(-1);
936 // Amg::Vector3D.eta() will crash via floating point exception if both x() and y() are zero (eta=inf)
937 // thus, check it manually here:
938 const Amg::Vector3D msVtxPos = MSRecoVx->getPosition();
939 if (msVtxPos.x() == 0 && msVtxPos.y() == 0 && msVtxPos.z() != 0) {
940 ATH_MSG_WARNING("given MSVertex has position x=y=0 and z!=0, eta() method will cause FPE, returning...");
941 return;
942 }
943
944 // MDTs -- count the number around the vertex
946 if (!mdtTES.isValid()) ATH_MSG_ERROR("Unable to retrieve the MDT hits");
947 int nmdt(0), nmdt_inwards(0), nmdt_I(0), nmdt_E(0), nmdt_M(0), nmdt_O(0);
948 // loop on the MDT collections, a collection corresponds to a chamber
949 for(const Muon::MdtPrepDataCollection* MDT_coll : *mdtTES){
950 if (MDT_coll->empty()) continue;
951 Muon::MdtPrepDataCollection::const_iterator mdtItr = MDT_coll->begin();
952 Amg::Vector3D ChamberCenter = (*mdtItr)->detectorElement()->center();
953 double deta = msVtxPos.eta() - ChamberCenter.eta();
954 if (std::abs(deta) > m_nMDTHitsEta) continue;
955 double dphi = xAOD::P4Helpers::deltaPhi(msVtxPos.phi(),ChamberCenter.phi());
956 if (std::abs(dphi) > m_nMDTHitsPhi) continue;
957
958 Identifier id = (*mdtItr)->identify();
959 stationRegion = m_idHelperSvc->mdtIdHelper().stationRegion(id);
960 auto [tubeLayerMin, tubeLayerMax] = m_idHelperSvc->mdtIdHelper().tubeLayerMinMax(id);
961 auto [tubeMin, tubeMax] = m_idHelperSvc->mdtIdHelper().tubeMinMax(id);
962 double nTubes = (tubeLayerMax - tubeLayerMin + 1) * (tubeMax - tubeMin + 1);
963
964 int nChHits(0), nChHits_inwards(0);
965 // loop on the MDT hits in the chamber
966 for (const Muon::MdtPrepData* mdt : *MDT_coll){
967 if (mdt->adc() < 50) continue;
968 if (mdt->status() != 1) continue;
969 if (mdt->localPosition()[Trk::locR] == 0.) continue;
970 ++nChHits;
971 if (mdt->globalPosition().mag() < msVtxPos.mag()) ++nChHits_inwards;
972 }
973 nmdt += nChHits;
974 nmdt_inwards += nChHits_inwards;
975 double ChamberOccupancy = nChHits / nTubes;
976 if (ChamberOccupancy > m_ChamberOccupancyMin) ++nHighOccupancy;
977
978 if (stationRegion == 0) nmdt_I += nChHits;
979 else if (stationRegion == 1) nmdt_E += nChHits;
980 else if (stationRegion == 2) nmdt_M += nChHits;
981 else if (stationRegion == 3) nmdt_O += nChHits;
982 }
983 ATH_MSG_DEBUG("Found " << nHighOccupancy << " chambers near the MS vertex with occupancy greater than " << m_ChamberOccupancyMin);
984 if (nHighOccupancy < m_minHighOccupancyChambers) return;
985
986 // RPC -- count the number around the vertex
988 if (!rpcTES.isValid()) ATH_MSG_ERROR("Unable to retrieve the RPC hits");
989 int nrpc(0), nrpc_inwards(0), nrpc_I(0), nrpc_E(0), nrpc_M(0), nrpc_O(0);
990 for (const Muon::RpcPrepDataCollection* RPC_coll : *rpcTES){
991 Muon::RpcPrepDataCollection::const_iterator rpcItr = RPC_coll->begin();
992 stationRegion = m_idHelperSvc->rpcIdHelper().stationRegion((*rpcItr)->identify());
993 int nChHits(0), nChHits_inwards(0);
994 for (const Muon::RpcPrepData* rpc : *RPC_coll){
995 double rpcEta = rpc->globalPosition().eta();
996 double rpcPhi = rpc->globalPosition().phi();
997 double DR = xAOD::P4Helpers::deltaR(msVtxPos.eta(), msVtxPos.phi(), rpcEta, rpcPhi);
998 if (DR < m_nTrigHitsdR) {
999 ++nChHits;
1000 if (rpc->globalPosition().mag() < msVtxPos.mag()) ++nChHits_inwards;
1001 }
1002 if (DR > 1.2) break;
1003 }
1004 nrpc += nChHits;
1005 nrpc_inwards += nChHits_inwards;
1006 if (stationRegion == 0) nrpc_I += nChHits;
1007 else if (stationRegion == 1) nrpc_E += nChHits;
1008 else if (stationRegion == 2) nrpc_M += nChHits;
1009 else if (stationRegion == 3) nrpc_O += nChHits;
1010 }
1011
1012 // TGC -- count the number around the vertex
1014 if (!tgcTES.isValid()) ATH_MSG_ERROR("Unable to retrieve the TGC hits");
1015 int ntgc(0), ntgc_inwards(0), ntgc_I(0), ntgc_E(0), ntgc_M(0), ntgc_O(0);
1016 for (const Muon::TgcPrepDataCollection* TGC_coll : *tgcTES){
1017 Muon::TgcPrepDataCollection::const_iterator tgcItr = TGC_coll->begin();
1018 stationRegion = m_idHelperSvc->tgcIdHelper().stationRegion((*tgcItr)->identify());
1019 int nChHits(0), nChHits_inwards(0);
1020 for (const Muon::TgcPrepData* tgc : *TGC_coll){
1021 double tgcEta = tgc->globalPosition().eta();
1022 double tgcPhi = tgc->globalPosition().phi();
1023 double DR = xAOD::P4Helpers::deltaR(msVtxPos.eta(), msVtxPos.phi(), tgcEta, tgcPhi);
1024 if (DR < m_nTrigHitsdR) {
1025 ++nChHits;
1026 if (tgc->globalPosition().mag() < msVtxPos.mag()) ++nChHits_inwards;
1027 }
1028 if (DR > 1.2) break;
1029 }
1030 ntgc += nChHits;
1031 ntgc_inwards += nChHits_inwards;
1032 if (stationRegion == 0) ntgc_I += nChHits;
1033 else if (stationRegion == 1) ntgc_E += nChHits;
1034 else if (stationRegion == 2) ntgc_M += nChHits;
1035 else if (stationRegion == 3) ntgc_O += nChHits;
1036 }
1037
1038 // store the hit counts in the MSVertex object
1039 MSRecoVx->setNMDT(nmdt, nmdt_inwards, nmdt_I, nmdt_E, nmdt_M, nmdt_O);
1040 MSRecoVx->setNRPC(nrpc, nrpc_inwards, nrpc_I, nrpc_E, nrpc_M, nrpc_O);
1041 MSRecoVx->setNTGC(ntgc, ntgc_inwards, ntgc_I, ntgc_E, ntgc_M, ntgc_O);
1042 }
1043
1044} // namespace Muon
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar perp() const
perp method - perpendicular length
Scalar phi() const
phi method
Scalar theta() const
theta method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double charge(const T &p)
Definition AtlasPID.h:997
#define AmgSymMatrix(dim)
static Double_t sz
double tubeMax
#define z
A wrapper class for event-slot-local random engines.
Definition RNGWrapper.h:56
void setSeed(const std::string &algName, const EventContext &ctx)
Set the random seed using a string (e.g.
Definition RNGWrapper.h:169
CLHEP::HepRandomEngine * getEngine(const EventContext &ctx) const
Retrieve the random engine corresponding to the provided EventContext.
Definition RNGWrapper.h:134
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
DataModel_detail::const_iterator< DataVector > const_iterator
Definition DataVector.h:838
const Amg::Vector3D & getPosition() const
Definition MSVertex.cxx:28
void setNRPC(const int, const int, const int, const int, const int, const int)
Definition MSVertex.cxx:54
void setNMDT(const int, const int, const int, const int, const int, const int)
Definition MSVertex.cxx:45
void setNTGC(const int, const int, const int, const int, const int, const int)
Definition MSVertex.cxx:63
Gaudi::Property< double > m_ChamberOccupancyMin
Gaudi::Property< double > m_nMDTHitsPhi
Gaudi::Property< bool > m_useOldMSVxEndcapMethod
void dressVtxHits(xAOD::Vertex *xAODVx, const std::vector< SG::AuxElement::Accessor< int > > &accs, const std::vector< int > &hits) const
Gaudi::Property< double > m_MaxZEndcap
virtual StatusCode initialize(void) override
Gaudi::Property< double > m_VertexMaxRadialPlane
Gaudi::Property< int > m_minHighOccupancyChambers
StatusCode findMSvertices(const std::vector< Tracklet > &tracklets, std::vector< std::unique_ptr< MSVertex > > &vertices, const EventContext &ctx) const override
double vxPhiFinder(const double theta, const double phi, const EventContext &ctx) const
Gaudi::Property< int > m_MinTrigHits
Gaudi::Property< double > m_nMDTHitsEta
Gaudi::Property< double > m_MaxTollDist
StatusCode FillOutputContainer(const std::vector< std::unique_ptr< MSVertex > > &, SG::WriteHandle< xAOD::VertexContainer > &xAODVxContainer) const
std::vector< Tracklet > RemoveBadTrk(const std::vector< Tracklet > &tracklets, const Amg::Vector3D &Vx) const
bool EndcapHasBadTrack(const std::vector< Tracklet > &tracklets, const Amg::Vector3D &Vx) const
std::vector< Tracklet > getTracklets(const std::vector< Tracklet > &trks, const std::set< int > &tracklet_subset) const
void MSStraightLineVx(const std::vector< Tracklet > &trks, std::unique_ptr< MSVertex > &vtx, const EventContext &ctx) const
Gaudi::Property< double > m_MinZEndcap
Gaudi::Property< double > m_VxChi2ProbCUT
std::optional< TrkCluster > ClusterizeTracks(std::vector< Tracklet > &tracks) const
SG::ReadHandleKey< Muon::MdtPrepDataContainer > m_mdtTESKey
Gaudi::Property< double > m_TrackPhiRotation
Gaudi::Property< unsigned int > m_maxClusterTracklets
Gaudi::Property< double > m_EndcapTrackletUncert
std::vector< SG::AuxElement::Accessor< int > > m_nRPC_accs
Gaudi::Property< double > m_ClusterdPhi
void MSVxFinder(const std::vector< Tracklet > &tracklets, std::unique_ptr< MSVertex > &vtx, const EventContext &ctx) const
SG::ReadHandleKey< Muon::RpcPrepDataContainer > m_rpcTESKey
void HitCounter(MSVertex *MSRecoVx, const EventContext &ctx) const
Gaudi::Property< unsigned int > m_maxGlobalTracklets
Gaudi::Property< double > m_TrackPhiAngle
Gaudi::Property< double > m_MaxTrackUncert
const std::vector< std::string > m_accMDT_str
Gaudi::Property< double > m_MaxLxyEndcap
Gaudi::Property< double > m_VertexMinRadialPlane
SG::ReadHandleKey< Muon::TgcPrepDataContainer > m_tgcTESKey
Gaudi::Property< bool > m_doSystematics
static Amg::Vector3D VxMinQuad(const std::vector< Tracklet > &tracks)
void MSStraightLineVx_oldMethod(const std::vector< Tracklet > &trks, std::unique_ptr< MSVertex > &vtx, const EventContext &ctx) const
std::vector< SG::AuxElement::Accessor< int > > m_nMDT_accs
Gaudi::Property< double > m_nTrigHitsdR
const std::vector< std::string > m_accTGC_str
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
std::vector< SG::AuxElement::Accessor< int > > m_nTGC_accs
Gaudi::Property< double > m_VxPlaneDist
ServiceHandle< IAthRNGSvc > m_rndmSvc
ToolHandle< Trk::IExtrapolator > m_extrapolator
SG::WriteHandleKey< xAOD::VertexContainer > m_xAODContainerKey
Gaudi::Property< double > m_BarrelTrackletUncert
MSVertexRecoTool(const std::string &type, const std::string &name, const IInterface *parent)
const std::vector< std::string > m_accRPC_str
Gaudi::Property< int > m_MinMDTHits
Gaudi::Property< double > m_ClusterdEta
std::vector< TrkCluster > findTrackClusters(const std::vector< Tracklet > &tracklets) const
Class to represent measurements from the Monitored Drift Tubes.
Definition MdtPrepData.h:33
Class to represent RPC measurements.
Definition RpcPrepData.h:35
Class to represent TGC measurements.
Definition TgcPrepData.h:32
void makePrivateStore()
Create a new (empty) private store for this object.
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:572
virtual bool isValid() override final
Can the handle be successfully dereferenced?
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
const xAOD::TrackParticle * getTrackParticle() const
Definition Tracklet.cxx:44
Class for a CylinderSurface in the ATLAS detector.
const Amg::Vector3D & momentum() const
Access method for the momentum.
Amg::Vector2D localPosition() const
Access method for the local coordinates, local parameter definitions differ for each surface type.
Class describing the Line to which the Perigee refers to.
void addTrackAtVertex(const ElementLink< TrackParticleContainer > &tr, float weight=1.0)
Add a new track to the vertex.
void setVertexType(VxType::VertexType vType)
Set the type of the vertex.
void setPosition(const Amg::Vector3D &position)
Sets the 3-position.
void setFitQuality(float chiSquared, float numberDoF)
Set the 'Fit Quality' information.
void Norm(TH1 *h, double scale)
Definition computils.cxx:67
double error(const Amg::MatrixX &mat, int index)
return diagonal error of the matrix caller should ensure the matrix is symmetric and the index is in ...
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
NRpcCablingAlg reads raw condition data and writes derived condition data to the condition store.
MuonPrepDataCollection< TgcPrepData > TgcPrepDataCollection
MuonPrepDataCollection< MdtPrepData > MdtPrepDataCollection
MuonPrepDataCollection< RpcPrepData > RpcPrepDataCollection
@ anyDirection
@ locY
local cartesian
Definition ParamDefs.h:38
@ locR
Definition ParamDefs.h:44
@ theta
Definition ParamDefs.h:66
ParametersT< TrackParametersDim, Charged, CylinderSurface > AtaCylinder
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
double deltaR(double rapidity1, double phi1, double rapidity2, double phi2)
from bare bare rapidity,phi
@ SecVtx
Secondary vertex.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Vertex_v1 Vertex
Define the latest version of the vertex class.
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".