ATLAS Offline Software
Loading...
Searching...
No Matches
MSVertexTrackletTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#include "TMath.h"
9
10/*
11 Tracklet reconstruction tool
12 See documentation at https://cds.cern.ch/record/1455664 and https://cds.cern.ch/record/1520894
13*/
14
15namespace Muon {
16
17 //** ----------------------------------------------------------------------------------------------------------------- **//
18
19 // Delta Alpha Constants -- p = k/(delta_alpha)
20 // inner and outer small stations don't have a from data determined constant. Instead a default value for DeltaAlpgaCut, momentum and momentum error is used.
21 constexpr double c_BIL = 28.4366; // MeV*mrad
22 constexpr double c_BMS = 53.1259; // MeV*mrad
23 constexpr double c_BML = 62.8267; // MeV*mrad
24 constexpr double c_BOL = 29.7554; // MeV*mrad
25 //** ----------------------------------------------------------------------------------------------------------------- **//
26
27 MSVertexTrackletTool::MSVertexTrackletTool(const std::string& type, const std::string& name, const IInterface* parent) :
28 AthAlgTool(type, name, parent) {
29 declareInterface<IMSVertexTrackletTool>(this);
30 }
31
32 //** ----------------------------------------------------------------------------------------------------------------- **//
33
35 ATH_CHECK(m_mdtTESKey.initialize());
36 ATH_CHECK(m_TPContainer.initialize());
37 ATH_CHECK(m_idHelperSvc.retrieve());
38
39 return StatusCode::SUCCESS;
40 }
41
42 //** ----------------------------------------------------------------------------------------------------------------- **//
43
44 StatusCode MSVertexTrackletTool::findTracklets(std::vector<Tracklet>& tracklets, const EventContext& ctx) const {
45 // record TrackParticle container in StoreGate
47 ATH_CHECK(container.record(std::make_unique<xAOD::TrackParticleContainer>(), std::make_unique<xAOD::TrackParticleAuxContainer>()));
48
49 // sort the MDT hits into chambers & MLs
50 std::vector<std::vector<const Muon::MdtPrepData*> > SortedMdt;
51
52 int nMDT = SortMDThits(SortedMdt, ctx);
53
54 if (nMDT <= 0) { return StatusCode::SUCCESS; }
55
56 if (msgLvl(MSG::DEBUG)) ATH_MSG_DEBUG("MDT hits are selected and sorted");
57
58 // loop over the MDT hits and find segments
59 // select the tube combinations to be fit
60 /*Select hits in at least 2 layers and require hits be ordered by increasing tube number (see diagrams below).
61 ( )( )(3)( ) ( )(3)( )( ) ( )( )( )( ) ( )( )(3)( ) ( )(2)(3)( )
62 ( )(2)( )( ) ( )(2)( )( ) ( )(2)(3)( ) ( )(1)(2)( ) ( )(1)( )( )
63 (1)( )( )( ) (1)( )( )( ) (1)( )( )( ) ( )( )( )( ) ( )( )( )( )
64 Barrel selection criteria: |z_mdt1 - z_mdt2| < m_d12_max (50 mm), |z_mdt1 - z_mdt3| < m_d13_max (80 mm)
65 Endcap selection criteria: |r_mdt1 - r_mdt2| < m_d12_max (50 mm), |r_mdt1 - r_mdt3| < m_d13_max (80 mm)
66 */
67
68 std::vector<TrackletSegment> segs[6][2][16]; // single ML segment array (indicies [station type][ML][sector]) with station type iterating through barrel inner, middle, outer and then endcap inner, middle, outer
69 std::vector<std::vector<const Muon::MdtPrepData*> >::const_iterator ChamberItr = SortedMdt.begin();
70 for (; ChamberItr != SortedMdt.end(); ++ChamberItr) {
71 std::vector<TrackletSegment> mlsegments;
72 std::vector<const Muon::MdtPrepData*>::const_iterator mdt1 = ChamberItr->begin();
73 std::vector<const Muon::MdtPrepData*>::const_iterator mdtEnd = ChamberItr->end();
74 if (IgnoreMDTChamber(*mdt1)) continue;
75
76 // get information about current chamber
77 Identifier mdt1_ID = (*mdt1)->identify();
78 bool mdt1_isBarrel = m_idHelperSvc->mdtIdHelper().isBarrel(mdt1_ID);
79 bool mdt1_isEndcap = m_idHelperSvc->mdtIdHelper().isEndcap(mdt1_ID);
80 int sector = m_idHelperSvc->sector(mdt1_ID);
81 int maxLayer = m_idHelperSvc->mdtIdHelper().tubeLayerMax(mdt1_ID);
82 int ML = m_idHelperSvc->mdtIdHelper().multilayer(mdt1_ID);
83
84 // loop on hits inside the chamber
85 for (; mdt1 != mdtEnd; ++mdt1) {
86 if (Amg::error((*mdt1)->localCovariance(), Trk::locR) < m_errorCutOff) {
87 ATH_MSG_WARNING(" " << m_idHelperSvc->mdtIdHelper().print_to_string(mdt1_ID) << " with too small error "
88 << Amg::error((*mdt1)->localCovariance(), Trk::locR));
89 continue;
90 }
91
92 int tl1 = m_idHelperSvc->mdtIdHelper().tubeLayer(mdt1_ID);
93 if (tl1 == maxLayer) break; // require hits in at least 2 layers
94
95 // loop on second hits
96 std::vector<const Muon::MdtPrepData*>::const_iterator mdt2 = (mdt1 + 1);
97 if (mdt2 == mdtEnd) continue;
98 Identifier mdt2_ID = (*mdt2)->identify();
99 for (; mdt2 != mdtEnd; ++mdt2) {
100 if (Amg::error((*mdt2)->localCovariance(), Trk::locR) < m_errorCutOff) {
101 ATH_MSG_WARNING(" " << m_idHelperSvc->mdtIdHelper().print_to_string(mdt2_ID)
102 << " with too small error " << Amg::error((*mdt2)->localCovariance(), Trk::locR));
103 continue;
104 }
105
106 // reject the bad tube combinations
107 int tl2 = m_idHelperSvc->mdtIdHelper().tubeLayer(mdt2_ID);
108 if (mdt1 == mdt2 || (tl2 - tl1) > 1 || (tl2 - tl1) < 0) continue;
109 if ((tl2 - tl1) == 0 && (m_idHelperSvc->mdtIdHelper().tube(mdt2_ID) -
110 m_idHelperSvc->mdtIdHelper().tube(mdt1_ID)) < 0) continue;
111 // reject bad hit separations
112 if (mdt1_isBarrel && std::abs((*mdt1)->globalPosition().z() - (*mdt2)->globalPosition().z()) > m_d12_max) continue;
113 if (mdt1_isEndcap && std::abs((*mdt1)->globalPosition().perp() - (*mdt2)->globalPosition().perp()) > m_d12_max) continue;
114
115 // loop on third hits
116 std::vector<const Muon::MdtPrepData*>::const_iterator mdt3 = (mdt2 + 1);
117 if (mdt3 == mdtEnd) continue;
118 Identifier mdt3_ID = (*mdt3)->identify();
119 for (; mdt3 != mdtEnd; ++mdt3) {
120 if (Amg::error((*mdt3)->localCovariance(), Trk::locR) < m_errorCutOff) {
121 ATH_MSG_WARNING(" " << m_idHelperSvc->mdtIdHelper().print_to_string(mdt3_ID)
122 << " with too small error " << Amg::error((*mdt3)->localCovariance(), Trk::locR));
123 continue;
124 }
125
126 // reject the bad tube combinations
127 if (mdt1 == mdt3 || mdt2 == mdt3) continue;
128 int tl3 = m_idHelperSvc->mdtIdHelper().tubeLayer(mdt3_ID);
129 if ((tl3 - tl2) > 1 || (tl3 - tl2) < 0 || (tl3 - tl1) <= 0) continue;
130 if ((tl3 - tl2) == 0 && (m_idHelperSvc->mdtIdHelper().tube(mdt3_ID) -
131 m_idHelperSvc->mdtIdHelper().tube(mdt2_ID)) < 0) continue;
132 // reject bad hit separations
133 if (mdt1_isBarrel && std::abs((*mdt1)->globalPosition().z() - (*mdt3)->globalPosition().z()) > m_d13_max) continue;
134 if (mdt1_isEndcap && std::abs((*mdt1)->globalPosition().perp() - (*mdt3)->globalPosition().perp()) > m_d13_max) continue;
135
136 // store and fit the good combinations
137 std::vector<const Muon::MdtPrepData*> mdts;
138 mdts.push_back((*mdt1));
139 mdts.push_back((*mdt2));
140 mdts.push_back((*mdt3));
141 std::vector<TrackletSegment> tmpSegs = TrackletSegmentFitter(mdts);
142 for (const TrackletSegment &tmpSeg : tmpSegs) mlsegments.push_back(tmpSeg);
143 } // end loop on mdt3
144 } // end loop on mdt2
145 } // end loop on mdt1
146
147 // store the reconstructed segments according to station, ML and sector
148 // MS region decoded in MuonIdHelpers/MuonIdHelper.h
149 int stationRegion = m_idHelperSvc->mdtIdHelper().stationRegion(mdt1_ID);
150 if (mdt1_isBarrel){
151 if (stationRegion == 0)
152 for (const TrackletSegment &mlsegment : mlsegments) segs[0][ML - 1][sector - 1].push_back(mlsegment); // barrel inner
153 else if (stationRegion == 2)
154 for (const TrackletSegment &mlsegment : mlsegments) segs[1][ML - 1][sector - 1].push_back(mlsegment); // barrel middle
155 else if (stationRegion == 3)
156 for (const TrackletSegment &mlsegment : mlsegments) segs[2][ML - 1][sector - 1].push_back(mlsegment); // barrel outer
157 }
158 else if (mdt1_isEndcap){
159 if (stationRegion == 0)
160 for (const TrackletSegment &mlsegment : mlsegments) segs[3][ML - 1][sector - 1].push_back(mlsegment); // endcap inner
161 else if (stationRegion == 2)
162 for (const TrackletSegment &mlsegment : mlsegments) segs[4][ML - 1][sector - 1].push_back(mlsegment); // endcap middle
163 else if (stationRegion == 3)
164 for (const TrackletSegment &mlsegment : mlsegments) segs[5][ML - 1][sector - 1].push_back(mlsegment); // endcap outer
165 }
166 else
167 ATH_MSG_WARNING("Found segments belonging to chamber " << m_idHelperSvc->mdtIdHelper().stationNameString(m_idHelperSvc->mdtIdHelper().stationName(mdt1_ID)) << " that have not been stored");
168 } // end loop on mdt chambers
169
170 // Combine/remove duplicate segments
171 std::vector<TrackletSegment> CleanSegs[6][2][16];
172 for (int st = 0; st < 6; ++st) {
173 for (int ml = 0; ml < 2; ++ml) {
174 for (int sector = 0; sector < 16; ++sector) {
175 if (!segs[st][ml][sector].empty()) {
176 CleanSegs[st][ml][sector] = CleanSegments(segs[st][ml][sector]);
177 }
178 }
179 }
180 }
181
182 // loop over TrackletSegments in barrel inner, middle, outer and endcap inner, middle, outer stations
183 for (int st = 0; st < 6; ++st) {
184 double DeltaAlphaCut = m_BarrelDeltaAlphaCut;
185 for (int sector = 0; sector < 16; ++sector) {
186 for (const TrackletSegment &ML1seg : CleanSegs[st][0][sector]) {
187 // Set the delta alpha cut depending on station type
188 const Identifier trkID = ML1seg.getIdentifier();
189 bool isBarrel = m_idHelperSvc->mdtIdHelper().isBarrel(trkID);
190 bool isSmall = m_idHelperSvc->mdtIdHelper().isSmall(trkID);
191 int stationRegion = m_idHelperSvc->mdtIdHelper().stationRegion(trkID);
192
193 if (isBarrel){
194 if (stationRegion == 0){
195 if (isSmall) DeltaAlphaCut = m_BarrelDeltaAlphaCut; // default value for BIS
196 else DeltaAlphaCut = c_BIL / 750.0;
197 }
198 else if (stationRegion == 2){
199 if (isSmall) DeltaAlphaCut = c_BMS / 750.0;
200 else DeltaAlphaCut = c_BML / 750.0;
201 }
202 else if (stationRegion == 3){
203 if (isSmall) DeltaAlphaCut = m_BarrelDeltaAlphaCut; // default value for BOS
204 else DeltaAlphaCut = c_BOL / 750.0;
205 }
206 }
207 else{
208 DeltaAlphaCut = m_EndcapDeltaAlphaCut;
209 }
210
211 // loop on ML2 segments from same sector
212 for (const TrackletSegment &ML2seg : CleanSegs[st][1][sector]) {
213 if (ML1seg.mdtChamber() != ML2seg.mdtChamber() || ML1seg.mdtChEta() != ML2seg.mdtChEta()) continue;
214
215 double deltaAlpha = ML1seg.alpha() - ML2seg.alpha();
216 bool goodDeltab = DeltabCalc(ML1seg, ML2seg);
217 // select the good combinations
218 if (std::abs(deltaAlpha) < DeltaAlphaCut && goodDeltab) {
219 if (isBarrel) {
220 // barrel chambers
221 double charge_discriminant = deltaAlpha * ML1seg.globalPosition().z() * std::tan(ML1seg.alpha());
222 double charge = charge_discriminant < 0 ? -1 : 1;
223
224 double pTot = TrackMomentum(ML1seg.getIdentifier(), deltaAlpha);
225 if (pTot < m_minpTot) continue;
226 if (pTot > m_maxpTot) {
227 // if we find a straight track, try to do a global refit to minimize the number of duplicates
228 charge = 0;
229 std::vector<const Muon::MdtPrepData*> mdts = ML1seg.mdtHitsOnTrack();
230 std::vector<const Muon::MdtPrepData*> mdts2 = ML2seg.mdtHitsOnTrack();
231 for (const Muon::MdtPrepData *mdt2 : mdts2) mdts.push_back(mdt2);
232 std::vector<TrackletSegment> CombinedSeg = TrackletSegmentFitter(mdts);
233
234 if (!CombinedSeg.empty()) {
235 // calculate momentum components & uncertainty
236 double Trk1overPErr = TrackMomentumError(CombinedSeg[0]);
237 double pT = pTot * std::sin(CombinedSeg[0].alpha());
238 double pz = pTot * std::cos(CombinedSeg[0].alpha());
239 Amg::Vector3D momentum(pT * std::cos(CombinedSeg[0].globalPosition().phi()),
240 pT * std::sin(CombinedSeg[0].globalPosition().phi()),
241 pz);
242 // create the error matrix
243 AmgSymMatrix(5) matrix;
244 matrix.setIdentity();
245 matrix(0, 0) = std::pow(CombinedSeg[0].rError(),2); // delta locR
246 matrix(1, 1) = std::pow(CombinedSeg[0].zError(),2); // delta locz
247 matrix(2, 2) = std::pow(0.00000000001,2); // delta phi (~0 because we explicitly rotate all tracklets into
248 // the middle of the chamber)
249 matrix(3, 3) = std::pow(CombinedSeg[0].alphaError(),2); // delta theta
250 matrix(4, 4) = std::pow(Trk1overPErr,2); // delta 1/p
251 Tracklet tmpTrk(CombinedSeg[0], momentum, matrix, charge);
252 ATH_MSG_DEBUG("Track " << tracklets.size() << " found with p = (" << momentum.x() << ", "
253 << momentum.y() << ", " << momentum.z()
254 << ") and |p| = " << tmpTrk.momentum().mag() << " MeV");
255 tracklets.push_back(tmpTrk);
256 }
257 } else {
258 // tracklet has a measurable momentum
259 double Trk1overPErr = TrackMomentumError(ML1seg, ML2seg);
260 double pT = pTot * std::sin(ML1seg.alpha());
261 double pz = pTot * std::cos(ML1seg.alpha());
262 Amg::Vector3D momentum(pT * std::cos(ML1seg.globalPosition().phi()),
263 pT * std::sin(ML1seg.globalPosition().phi()),
264 pz);
265 // create the error matrix
266 AmgSymMatrix(5) matrix;
267 matrix.setIdentity();
268 matrix(0, 0) = std::pow(ML1seg.rError(),2); // delta locR
269 matrix(1, 1) = std::pow(ML1seg.zError(),2); // delta locz
270 matrix(2, 2) = std::pow(0.00000000001,2); // delta phi (~0 because we explicitly rotate all tracks into the
271 // middle of the chamber)
272 matrix(3, 3) = std::pow(ML1seg.alphaError(),2); // delta theta
273 matrix(4, 4) = std::pow(Trk1overPErr,2); // delta 1/p
274 Tracklet tmpTrk(ML1seg, ML2seg, momentum, matrix, charge);
275 ATH_MSG_DEBUG("Track " << tracklets.size() << " found with p = (" << momentum.x() << ", "
276 << momentum.y() << ", " << momentum.z()
277 << ") and |p| = " << tmpTrk.momentum().mag() << " MeV");
278 tracklets.push_back(tmpTrk);
279 }
280 } // end barrel chamber selection
281 else if (!isBarrel) {
282 // endcap tracklets
283 // always straight tracklets (no momentum measurement possible)
284 std::vector<const Muon::MdtPrepData*> mdts = ML1seg.mdtHitsOnTrack();
285 std::vector<const Muon::MdtPrepData*> mdts2 = ML2seg.mdtHitsOnTrack();
286 for (const Muon::MdtPrepData *mdt2 : mdts2) mdts.push_back(mdt2);
287 std::vector<TrackletSegment> CombinedSeg = TrackletSegmentFitter(mdts);
288
289 if (!CombinedSeg.empty()) {
290 double charge = 0;
291 double pTot = m_straightTrackletpTot;
292 double pT = pTot * std::sin(CombinedSeg[0].alpha());
293 double pz = pTot * std::cos(CombinedSeg[0].alpha());
294 Amg::Vector3D momentum(pT * std::cos(CombinedSeg[0].globalPosition().phi()),
295 pT * std::sin(CombinedSeg[0].globalPosition().phi()),
296 pz);
297 // create the error matrix
298 AmgSymMatrix(5) matrix;
299 matrix.setIdentity();
300 matrix(0, 0) = std::pow(CombinedSeg[0].rError(),2); // delta locR
301 matrix(1, 1) = std::pow(CombinedSeg[0].zError(),2); // delta locz
302 matrix(2, 2) = std::pow(0.0000001,2); // delta phi (~0 because we explicitly rotate all tracks into the middle
303 // of the chamber)
304 matrix(3, 3) = std::pow(CombinedSeg[0].alphaError(),2); // delta theta
305 matrix(4, 4) = std::pow(m_straightTrackletInvPerr,2); // delta 1/p (endcap tracks are straight lines with no momentum that we can measure ...)
306
307 Tracklet tmpTrk(CombinedSeg[0], momentum, matrix, charge);
308 tracklets.push_back(tmpTrk);
309 }
310 } // end endcap tracklet selection
311
312 } // end tracklet selection (delta alpha & delta b)
313
314 } // end loop on ML2 segments
315 } // end loop on ML1 segments
316 } // end loop on sectors
317 } // end loop on stations
318
319 // Resolve any ambiguous tracklets
320 tracklets = ResolveAmbiguousTracklets(tracklets);
321
322 // convert from tracklets to Trk::Tracks
323 convertToTrackParticles(tracklets, container);
324
325 return StatusCode::SUCCESS;
326 }
327
328 //** ----------------------------------------------------------------------------------------------------------------- **//
329
330 void MSVertexTrackletTool::convertToTrackParticles(std::vector<Tracklet>& tracklets,
332 // convert tracklets to xAOD::TrackParticle and store in a TrackCollection
333 for (Tracklet &tracklet : tracklets) {
334 xAOD::TrackParticle* trackparticle = new xAOD::TrackParticle();
335 tracklet.setTrackParticle(trackparticle);
336 container->push_back(trackparticle);
337
338 AmgSymMatrix(5) covariance{tracklet.errorMatrix()};
339 auto MyPerigee(std::make_unique<Trk::Perigee>(tracklet.globalPosition(), tracklet.momentum(), tracklet.charge(), Trk::PerigeeSurface(Amg::Vector3D::Zero()), covariance));
340
341 // fill the xAOD::TrackParticle with the tracklet content
342 trackparticle->setDefiningParameters(MyPerigee->parameters()[Trk::d0], MyPerigee->parameters()[Trk::z0],
343 MyPerigee->parameters()[Trk::phi0], MyPerigee->parameters()[Trk::theta],
344 MyPerigee->parameters()[Trk::qOverP]);
345 trackparticle->setFitQuality(1., (float)tracklet.mdtHitsOnTrack().size());
347 std::vector<float> covMatrixVec;
348 Amg::compress(covariance, covMatrixVec);
349 trackparticle->setDefiningParametersCovMatrixVec(covMatrixVec);
350 }
351 return;
352 }
353
354 //** ----------------------------------------------------------------------------------------------------------------- **//
355
357 // return true if the MDT hit is in a chamber to be ignored. These hits are then not used to reconstruct tracklets.
358
359 bool ignore = false;
360 int stName = m_idHelperSvc->mdtIdHelper().stationName(mdtHit->identify());
361 int stEta = m_idHelperSvc->mdtIdHelper().stationEta(mdtHit->identify());
362
363 // Doesn't consider hits belonging to chambers BEE, EEL and EES
364 if (stName == m_idHelperSvc->mdtIdHelper().stationNameIndex("BEE") ||
365 stName == m_idHelperSvc->mdtIdHelper().stationNameIndex("EEL") ||
366 stName == m_idHelperSvc->mdtIdHelper().stationNameIndex("EES")) ignore = true;
367
368 // Doesn't consider hits belonging to chambers BIS7/8
369 if (stName == m_idHelperSvc->mdtIdHelper().stationNameIndex("BIS") && std::abs(stEta) >= 7) ignore = true;
370
371 // Doesn't consider hits belonging to BME or BMG chambers
372 if (stName == m_idHelperSvc->mdtIdHelper().stationNameIndex("BME") ||
373 stName == m_idHelperSvc->mdtIdHelper().stationNameIndex("BMG")) ignore = true;
374
375 return ignore;
376 }
377
378 //** ----------------------------------------------------------------------------------------------------------------- **//
379
380 int MSVertexTrackletTool::SortMDThits(std::vector<std::vector<const Muon::MdtPrepData*> >& SortedMdt, const EventContext& ctx) const {
381 SortedMdt.clear();
382 int nMDT(0);
383
385 if (!mdtTES.isValid()) {
386 if (msgLvl(MSG::DEBUG)) msg(MSG::DEBUG) << "Muon::MdtPrepDataContainer with key MDT_DriftCircles was not retrieved" << endmsg;
387 return 0;
388 } else {
389 if (msgLvl(MSG::DEBUG)) msg(MSG::DEBUG) << "Muon::MdtPrepDataContainer with key MDT_DriftCircles retrieved" << endmsg;
390 }
391
392 // iterators over collections, a collection corresponds to a chamber
393 for (const Muon::MdtPrepDataCollection* MDTch : *mdtTES){
394 if (MDTch->empty()) continue;
395 if (IgnoreMDTChamber(*(MDTch->begin()))) continue;
396
397 // sort per multi layer
398 std::vector<const Muon::MdtPrepData*> hitsML1;
399 std::vector<const Muon::MdtPrepData*> hitsML2;
400
401 // loop on mdt hits in the current chamber
402 for (const Muon::MdtPrepData* mdt : *MDTch) {
403 // Removes noisy hits
404 if (mdt->adc() < 50) continue;
405 // Removes dead modules or out of time hits
406 if (mdt->status() != Muon::MdtStatusDriftTime) continue;
407 // Removes tubes out of readout during drift time or with unphysical errors
408 if (mdt->localPosition()[Trk::locR] == 0.) continue;
409 if (mdt->localCovariance()(Trk::locR, Trk::locR) < 1e-6) {
410 ATH_MSG_WARNING("Found MDT with unphysical error " << m_idHelperSvc->mdtIdHelper().print_to_string(mdt->identify())
411 << " cov " << mdt->localCovariance()(Trk::locR, Trk::locR));
412 continue;
413 }
414 ++nMDT;
415
416 // sort per multi layer
417 if (m_idHelperSvc->mdtIdHelper().multilayer(mdt->identify()) == 1)
418 hitsML1.push_back(mdt);
419 else
420 hitsML2.push_back(mdt);
421
422 } // end MdtPrepDataCollection
423
424 // add
425 addMDTHits(hitsML1, SortedMdt);
426 addMDTHits(hitsML2, SortedMdt);
427 } // end MdtPrepDataContainer
428
429 return nMDT;
430 }
431
432 void MSVertexTrackletTool::addMDTHits(std::vector<const Muon::MdtPrepData*>& hits,
433 std::vector<std::vector<const Muon::MdtPrepData*> >& SortedMdt) const {
434 if (hits.empty()) return;
435
436 // calculate number of hits in ML
437 int ntubes = hits.front()->detectorElement()->getNLayers() * hits.front()->detectorElement()->getNtubesperlayer();
438 if (hits.size() > 0.75 * ntubes) return;
439 std::sort(hits.begin(), hits.end(), [this](const Muon::MdtPrepData* mprd1, const Muon::MdtPrepData* mprd2) -> bool {
440 if (m_idHelperSvc->mdtIdHelper().tubeLayer(mprd1->identify()) > m_idHelperSvc->mdtIdHelper().tubeLayer(mprd2->identify()))
441 return false;
442 if (m_idHelperSvc->mdtIdHelper().tubeLayer(mprd1->identify()) < m_idHelperSvc->mdtIdHelper().tubeLayer(mprd2->identify()))
443 return true;
444 if (m_idHelperSvc->mdtIdHelper().tube(mprd1->identify()) < m_idHelperSvc->mdtIdHelper().tube(mprd2->identify())) return true;
445 return false;
446 }); // sort the MDTs by layer and tube number
447
448 SortedMdt.push_back(hits);
449 }
450
451 //** ----------------------------------------------------------------------------------------------------------------- **//
452
453 std::vector<TrackletSegment> MSVertexTrackletTool::TrackletSegmentFitter(const std::vector<const Muon::MdtPrepData*>& mdts) const {
454 // fits TrackletSegments from an as compatible identified set of MDT hits
455 // create the segment seeds
456 std::vector<std::pair<double, double> > SeedParams = SegSeeds(mdts);
457 // fit the segments
458 std::vector<TrackletSegment> segs = TrackletSegmentFitterCore(mdts, SeedParams);
459
460 return segs;
461 }
462
463 //** ----------------------------------------------------------------------------------------------------------------- **//
464
465 std::vector<std::pair<double, double> > MSVertexTrackletTool::SegSeeds(const std::vector<const Muon::MdtPrepData*>& mdts) const {
466 std::vector<std::pair<double, double> > SeedParams;
467 if (mdts.empty())[[unlikely]]{
468 ATH_MSG_DEBUG("SegSeeds called with an empty vector.");
469 return SeedParams;
470 }
471 // create seeds by drawing the 4 possible lines tangent to the two outermost drift circles
472 // see http://cds.cern.ch/record/620198 (section 4.3) for description of the algorithm
473 // keep all seeds which satisfy the criterion: residual(mdt 2) < m_SeedResidual
474 // NOTE: here there is an assumption that each MDT has a radius of 30mm
475 // -- needs to be revisited when the small tubes in sectors 12 & 14 are installed
476 double x1 = mdts.front()->globalPosition().z();
477 double y1 = mdts.front()->globalPosition().perp();
478 double r1 = std::abs(mdts.front()->localPosition()[Trk::locR]);
479
480 double x2 = mdts.back()->globalPosition().z();
481 double y2 = mdts.back()->globalPosition().perp();
482 double r2 = std::abs(mdts.back()->localPosition()[Trk::locR]);
483
484 double DeltaX = x2 - x1;
485 double DeltaY = y2 - y1;
486 double DistanceOfCenters = std::hypot(DeltaX, DeltaY);
487 if (DistanceOfCenters < 30) return SeedParams;
488 double Alpha0 = std::acos(DeltaX / DistanceOfCenters);
489
490 // First seed
491 double phi = mdts.front()->globalPosition().phi();
492 double RSum = r1 + r2;
493 if (RSum > DistanceOfCenters) return SeedParams;
494 double Alpha1 = std::asin(RSum / DistanceOfCenters);
495 double line_theta = Alpha0 + Alpha1;
496 double z_line = x1 + r1 * std::sin(line_theta);
497 double rho_line = y1 - r1 * std::cos(line_theta);
498
499 Amg::Vector3D gPos1(rho_line * std::cos(phi), rho_line * std::sin(phi), z_line);
500 Amg::Vector3D gDir(std::cos(phi) * std::sin(line_theta), std::sin(phi) * std::sin(line_theta), std::cos(line_theta));
501 Amg::Vector3D globalDir1(std::cos(phi) * std::sin(line_theta), std::sin(phi) * std::sin(line_theta), std::cos(line_theta));
502 double gSlope1 = (globalDir1.perp() / globalDir1.z());
503 double gInter1 = gPos1.perp() - gSlope1 * gPos1.z();
504 double resid = SeedResiduals(mdts, gSlope1, gInter1);
505 if (resid < m_SeedResidual) SeedParams.emplace_back(gSlope1, gInter1);
506 // Second seed
507 line_theta = Alpha0 - Alpha1;
508 z_line = x1 - r1 * std::sin(line_theta);
509 rho_line = y1 + r1 * std::cos(line_theta);
510 Amg::Vector3D gPos2(rho_line * std::cos(phi), rho_line * std::sin(phi), z_line);
511 Amg::Vector3D globalDir2(std::cos(phi) * std::sin(line_theta), std::sin(phi) * std::sin(line_theta), std::cos(line_theta));
512 double gSlope2 = (globalDir2.perp() / globalDir2.z());
513 double gInter2 = gPos2.perp() - gSlope2 * gPos2.z();
514 resid = SeedResiduals(mdts, gSlope2, gInter2);
515 if (resid < m_SeedResidual) SeedParams.emplace_back(gSlope2, gInter2);
516
517 double Alpha2 = std::asin(std::abs(r2 - r1) / DistanceOfCenters);
518 if (r1 < r2) {
519 // Third seed
520 line_theta = Alpha0 + Alpha2;
521 z_line = x1 - r1 * std::sin(line_theta);
522 rho_line = y1 + r1 * std::cos(line_theta);
523
524 Amg::Vector3D gPos3(rho_line * std::cos(phi), rho_line * std::sin(phi), z_line);
525 Amg::Vector3D globalDir3(std::cos(phi) * std::sin(line_theta), std::sin(phi) * std::sin(line_theta), std::cos(line_theta));
526 double gSlope3 = (globalDir3.perp() / globalDir3.z());
527 double gInter3 = gPos3.perp() - gSlope3 * gPos3.z();
528 resid = SeedResiduals(mdts, gSlope3, gInter3);
529 if (resid < m_SeedResidual) SeedParams.emplace_back(gSlope3, gInter3);
530
531 // Fourth seed
532 line_theta = Alpha0 - Alpha2;
533 z_line = x1 + r1 * std::sin(line_theta);
534 rho_line = y1 - r1 * std::cos(line_theta);
535
536 Amg::Vector3D gPos4(rho_line * std::cos(phi), rho_line * std::sin(phi), z_line);
537 Amg::Vector3D globalDir4(std::cos(phi) * std::sin(line_theta), std::sin(phi) * std::sin(line_theta), std::cos(line_theta));
538 double gSlope4 = (globalDir4.perp() / globalDir4.z());
539 double gInter4 = gPos4.perp() - gSlope4 * gPos4.z();
540 resid = SeedResiduals(mdts, gSlope4, gInter4);
541 if (resid < m_SeedResidual) SeedParams.emplace_back(gSlope4, gInter4);
542 } else {
543 // Third seed
544 line_theta = Alpha0 + Alpha2;
545 z_line = x1 + r1 * std::sin(line_theta);
546 rho_line = y1 - r1 * std::cos(line_theta);
547
548 Amg::Vector3D gPos3(rho_line * std::cos(phi), rho_line * std::sin(phi), z_line);
549 Amg::Vector3D globalDir3(std::cos(phi) * std::sin(line_theta), std::sin(phi) * std::sin(line_theta), std::cos(line_theta));
550 double gSlope3 = (globalDir3.perp() / globalDir3.z());
551 double gInter3 = gPos3.perp() - gSlope3 * gPos3.z();
552 resid = SeedResiduals(mdts, gSlope3, gInter3);
553 if (resid < m_SeedResidual) SeedParams.emplace_back(gSlope3, gInter3);
554
555 // Fourth seed
556 line_theta = Alpha0 - Alpha2;
557 z_line = x1 - r1 * std::sin(line_theta);
558 rho_line = y1 + r1 * std::cos(line_theta);
559
560 Amg::Vector3D gPos4(rho_line * std::cos(phi), rho_line * std::sin(phi), z_line);
561 Amg::Vector3D globalDir4(std::cos(phi) * std::sin(line_theta), std::sin(phi) * std::sin(line_theta), std::cos(line_theta));
562 double gSlope4 = (globalDir4.perp() / globalDir4.z());
563 double gInter4 = gPos4.perp() - gSlope4 * gPos4.z();
564 resid = SeedResiduals(mdts, gSlope4, gInter4);
565 if (resid < m_SeedResidual) SeedParams.emplace_back(gSlope4, gInter4);
566 }
567
568 return SeedParams;
569 }
570
571 //** ----------------------------------------------------------------------------------------------------------------- **//
572
573 double MSVertexTrackletTool::SeedResiduals(const std::vector<const Muon::MdtPrepData*>& mdts, double slope, double inter) {
574 // calculate the residual of the MDTs not used to create the seed
575 double resid = 0;
576 for (const Muon::MdtPrepData *mdt : mdts) {
577 double mdtR = mdt->globalPosition().perp();
578 double mdtZ = mdt->globalPosition().z();
579 double res = std::abs((mdt->localPosition()[Trk::locR] - std::abs((mdtR - inter - slope * mdtZ) / std::hypot(slope, 1))) /
580 (Amg::error(mdt->localCovariance(), Trk::locR)));
581 if (res > resid) resid = res;
582 }
583 return resid;
584 }
585
586 //** ----------------------------------------------------------------------------------------------------------------- **//
587
588 std::vector<TrackletSegment> MSVertexTrackletTool::TrackletSegmentFitterCore(const std::vector<const Muon::MdtPrepData*>& mdts,
589 const std::vector<std::pair<double, double> >& SeedParams) const {
590 std::vector<TrackletSegment> segs;
591
592 Identifier mdtID = mdts.at(0)->identify();
593 for (const std::pair<double,double> &SeedParam : SeedParams) {
594 // Min chi^2 fit from "Precision of the ATLAS Muon Spectrometer" -- M. Woudstra
595 // http://cds.cern.ch/record/620198?ln=en (section 4.3)
596 double chi2(0);
597 double s(0), sz(0), sy(0);
598 // loop on the mdt hits, find the weighted center
599 for (const Muon::MdtPrepData *prd : mdts) {
600 // Tell clang to optimize assuming that FP exceptions can trap.
601 // Otherwise, it can vectorize the division, which can lead to
602 // spurious division-by-zero traps from unused vector lanes.
604 const double mdt_y = std::hypot(prd->globalPosition().x(), prd->globalPosition().y());
605 const double mdt_z = prd->globalPosition().z();
606 const double sigma2 = std::pow(Amg::error(prd->localCovariance(), Trk::locR),2);
607 s += 1 / sigma2;
608 sz += mdt_z / sigma2;
609 sy += mdt_y / sigma2;
610 }
611 if ( s == 0.)[[unlikely]]{
612 ATH_MSG_ERROR("s == 0.");
613 continue;
614 }
615 const double yc = sy / s;
616 const double zc = sz / s;
617
618 // Find the initial parameters of the fit
619 double alpha = std::atan2(SeedParam.first, 1.0);
620 if (alpha < 0) alpha += M_PI;
621 double dalpha = 0;
622 double d = (SeedParam.second - yc + zc * SeedParam.first) * std::cos(alpha);
623 double dd = 0;
624
625 // require segments to point to the second ML
626 if (std::abs(std::cos(alpha)) > 0.97 && (m_idHelperSvc->mdtIdHelper().isBarrel(mdtID))) continue;
627 if (std::abs(std::cos(alpha)) < 0.03 && (m_idHelperSvc->mdtIdHelper().isEndcap(mdtID))) continue;
628
629 // calculate constants used in the fit
630 double sPyy(0), sPyz(0), sPyyzz(0);
631 for (const Muon::MdtPrepData *prd : mdts) {
632 double mdt_y = std::hypot(prd->globalPosition().x(), prd->globalPosition().y());
633 double mdt_z = prd->globalPosition().z();
634 double sigma2 = std::pow(Amg::error(prd->localCovariance(), Trk::locR),2);
635 sPyy += std::pow(mdt_y-yc,2) / sigma2;
636 sPyz += (mdt_y - yc) * (mdt_z - zc) / sigma2;
637 sPyyzz += ((mdt_y - yc) - (mdt_z - zc)) * ((mdt_y - yc) + (mdt_z - zc)) / sigma2;
638 }
639
640 // iterative fit
641 int Nitr = 0;
642 double deltaAlpha = 0;
643 double deltad = 0;
644 while (true) {
645 double sumRyi(0), sumRzi(0), sumRi(0);
646 chi2 = 0;
647 ++Nitr;
648 const double cos_a = std::cos(alpha);
649 const double sin_a = std::sin(alpha);
650 for (const Muon::MdtPrepData *prd : mdts) {
651 double mdt_y = prd->globalPosition().perp();
652 double mdt_z = prd->globalPosition().z();
653 double yPi = -(mdt_z - zc) * sin_a + (mdt_y - yc) * cos_a - d;
654 double signR = yPi >= 0 ? -1. : 1;
655 double sigma2 = std::pow(Amg::error(prd->localCovariance(), Trk::locR),2);
656 double ri = signR * prd->localPosition()[Trk::locR];
657 sumRyi += ri * (mdt_y - yc) / sigma2;
658 sumRzi += ri * (mdt_z - zc) / sigma2;
659 sumRi += ri / sigma2;
660 chi2 += std::pow(yPi+ri,2) / sigma2;
661 }
662 double bAlpha = -1 * sPyz + cos_a * (sin_a * sPyyzz + 2 * cos_a * sPyz + sumRzi) + sin_a * sumRyi;
663 double AThTh = sPyy + cos_a * (2 * sin_a * sPyz - cos_a * sPyyzz);
667 if (std::abs(AThTh) < 1.e-7) break;
668 // the new alpha & d parameters
669 double alphaNew = alpha + bAlpha / AThTh;
670 double dNew = sumRi / s;
671 // the errors
672 dalpha = std::sqrt(1 / std::abs(AThTh));
673 dd = std::sqrt(1 / s);
674 deltaAlpha = std::abs(alphaNew - alpha);
675 deltad = std::abs(d - dNew);
676 // test if the new segment is different than the previous
677 if (deltaAlpha < 5.e-7 && deltad < 5.e-6) break;
678 alpha = alphaNew;
679 d = dNew;
680 // Guard against infinite loops
681 if (Nitr > 10) break;
682 } // end while loop
683
684 // find the chi^2 probability of the segment
685 double chi2Prob = TMath::Prob(chi2, mdts.size() - 2);
686 // keep only "good" segments
687 if (chi2Prob > m_minSegFinderChi2) {
688 double z0 = zc - d * std::sin(alpha);
689 double dz0 = std::hypot(dd*std::sin(alpha), d*dalpha*std::cos(alpha));
690 double y0 = yc + d * std::cos(alpha);
691 double dy0 = std::hypot(dd*std::cos(alpha), d*dalpha*std::sin(alpha));
692 // find the hit pattern, which side of the wire did the particle pass? (1==Left, 2==Right)
693 /*
694 ( )(/O)( )
695 (./)( )( ) == RRL == 221
696 (O/)( )( )
697 */
698 int pattern(0);
699 if (mdts.size() > 8)
700 pattern = -1; // with more then 8 MDTs the pattern is unique
701 else {
702 for (unsigned int k = 0; k < mdts.size(); ++k) {
703 int base = std::pow(10, k);
704 double mdtR = std::hypot(mdts.at(k)->globalPosition().x(), mdts.at(k)->globalPosition().y());
705 double mdtZ = mdts.at(k)->globalPosition().z();
706 double zTest = (mdtR - y0) / std::tan(alpha) + z0 - mdtZ;
707 if (zTest > 0)
708 pattern += 2 * base;
709 else
710 pattern += base;
711 }
712 }
713
714 // find the position of the tracklet in the global frame
715 double mdtPhi = mdts.at(0)->globalPosition().phi();
716 Amg::Vector3D segpos(y0 * std::cos(mdtPhi), y0 * std::sin(mdtPhi), z0);
717 // create the tracklet
718 TrackletSegment MyTrackletSegment{m_idHelperSvc.get(), mdts, segpos, alpha, dalpha, dy0, dz0, pattern};
719 segs.push_back(MyTrackletSegment);
720 if (pattern == -1) break; // stop if we find a segment with more than 8 hits (guaranteed to be unique!)
721 }
722 } // end loop on segment seeds
723
724 // in case more than 1 segment is reconstructed, check if there are duplicates using the hit patterns
725 if (segs.size() > 1) {
726 std::vector<TrackletSegment> tmpSegs;
727 for (unsigned int i1 = 0; i1 < segs.size(); ++i1) {
728 bool isUnique = true;
729 int pattern1 = segs.at(i1).getHitPattern();
730 for (unsigned int i2 = (i1 + 1); i2 < segs.size(); ++i2) {
731 if (pattern1 == -1) break;
732 int pattern2 = segs.at(i2).getHitPattern();
733 if (pattern1 == pattern2) isUnique = false;
734 }
735 if (isUnique) tmpSegs.push_back(segs.at(i1));
736 }
737 segs = std::move(tmpSegs);
738 }
739
740 // return the unique segments
741 return segs;
742 }
743
744 //** ----------------------------------------------------------------------------------------------------------------- **//
745
746 std::vector<TrackletSegment> MSVertexTrackletTool::CleanSegments(const std::vector<TrackletSegment>& Segs) const {
747 std::vector<TrackletSegment> CleanSegs;
748 std::vector<TrackletSegment> segs = Segs; // set of segments to perform cleaning on
749 bool keepCleaning(true);
750 int nItr(0);
751
752 while (keepCleaning) {
753 ++nItr;
754 keepCleaning = false;
755
756 for (std::vector<TrackletSegment>::iterator it = segs.begin(); it != segs.end(); ++it) {
757 if (it->isCombined()) continue;
758 std::vector<TrackletSegment> segsToCombine;
759 double tanTh1 = std::tan(it->alpha());
760 double r1 = it->globalPosition().perp();
761 double zi1 = it->globalPosition().z() - r1 / tanTh1;
762 // find all segments with similar parameters & attempt to combine
763 for (std::vector<TrackletSegment>::iterator sit = (it + 1); sit != segs.end(); ++sit) {
764 if (sit->isCombined()) continue;
765 if (it->mdtChamber() != sit->mdtChamber()) continue; // require the segments are in the same chamber
766 if ((it->mdtChEta()) * (sit->mdtChEta()) < 0) continue; // check both segments are on the same side of the detector
767 if (it->mdtChPhi() != sit->mdtChPhi()) continue; // in the same sector
768 if (std::abs(it->alpha() - sit->alpha()) > 0.005) continue; // same trajectory
769 double tanTh2 = std::tan(sit->alpha());
770 double r2 = sit->globalPosition().perp();
771 double zi2 = sit->globalPosition().z() - r2 / tanTh2;
772 // find the distance at the midpoint between the two segments
773 double rmid = (r1 + r2) / 2.;
774 double z1 = rmid / tanTh1 + zi1;
775 double z2 = rmid / tanTh2 + zi2;
776 double zdist = std::abs(z1 - z2);
777 if (zdist < 0.5) {
778 segsToCombine.push_back(*sit);
779 sit->isCombined(true);
780 }
781 } // end sit loop
782
783 // if the segment is unique, keep it
784 if (segsToCombine.empty()) {
785 CleanSegs.push_back(*it);
786 }
787 // else, combine all like segments & refit
788 else if (!segsToCombine.empty()) {
789 // create a vector of all unique MDT hits in the segments
790 std::vector<const Muon::MdtPrepData*> mdts = it->mdtHitsOnTrack();
791 for (const TrackletSegment &seg : segsToCombine) {
792 std::vector<const Muon::MdtPrepData*> tmpmdts = seg.mdtHitsOnTrack();
793 for (const Muon::MdtPrepData *tmpprd : tmpmdts){
794 bool isNewHit(true);
795 for (const Muon::MdtPrepData *tmpprd2 : mdts){
796 if (tmpprd->identify() == tmpprd2->identify()) {
797 isNewHit = false;
798 break;
799 }
800 }
801 if (isNewHit && Amg::error(tmpprd->localCovariance(), Trk::locR) > m_errorCutOff) mdts.push_back(tmpprd);
802 }
803 } // end segsToCombine loop
804
805 // only need to combine if there are extra hits added to the first segment
806 if (mdts.size() > it->mdtHitsOnTrack().size()) {
807 std::vector<TrackletSegment> refitsegs = TrackletSegmentFitter(mdts);
808 // if the refit fails, what to do?
809 if (refitsegs.empty()) {
810 if (segsToCombine.size() == 1) {
811 segsToCombine[0].isCombined(false);
812 CleanSegs.push_back(*it);
813 CleanSegs.push_back(segsToCombine[0]);
814 } else {
815 // loop on the mdts and count the number of segments that share that hit
816 std::vector<int> nSeg;
817 for (unsigned int i = 0; i < mdts.size(); ++i) {
818 nSeg.push_back(0);
819 // hit belongs to the first segment
820 for (unsigned int k = 0; k < it->mdtHitsOnTrack().size(); ++k) {
821 if (it->mdtHitsOnTrack()[k]->identify() == mdts[i]->identify()) {
822 ++nSeg[i];
823 break;
824 }
825 }
826 // hit belongs to one of the duplicate segments
827 for (unsigned int k = 0; k < segsToCombine.size(); ++k) {
828 for (unsigned int m = 0; m < segsToCombine[k].mdtHitsOnTrack().size(); ++m) {
829 if (segsToCombine[k].mdtHitsOnTrack()[m]->identify() == mdts[i]->identify()) {
830 ++nSeg[i];
831 break;
832 }
833 } // end loop on mdtHitsOnTrack
834 } // end loop on segsToCombine
835 } // end loop on mdts
836
837 // loop over the duplicates and remove the MDT used by the fewest segments until the fit converges
838 bool keeprefitting(true);
839 int nItr2(0);
840 while (keeprefitting) {
841 ++nItr2;
842 int nMinSeg(nSeg[0]);
843 const Muon::MdtPrepData* minmdt = mdts[0];
844 std::vector<int> nrfsegs;
845 std::vector<const Muon::MdtPrepData*> refitmdts;
846 // loop on MDTs, identify the overlapping set of hits
847 for (unsigned int i = 1; i < mdts.size(); ++i) {
848 if (nSeg[i] < nMinSeg) {
849 refitmdts.push_back(minmdt);
850 nrfsegs.push_back(nMinSeg);
851 minmdt = mdts[i];
852 nMinSeg = nSeg[i];
853 } else {
854 refitmdts.push_back(mdts[i]);
855 nrfsegs.push_back(nSeg[i]);
856 }
857 }
858 // reset the list of MDTs & the minimum number of segments an MDT must belong to
859 mdts = std::move(refitmdts);
860 nSeg = std::move(nrfsegs);
861 // try to fit the new set of MDTs
862 refitsegs = TrackletSegmentFitter(mdts);
863 if (!refitsegs.empty()) {
864 for (const TrackletSegment &refitseg : refitsegs) CleanSegs.push_back(refitseg);
865 keeprefitting = false; // stop refitting if segments are found
866 } else if (mdts.size() <= 3) {
867 CleanSegs.push_back(*it);
868 keeprefitting = false;
869 }
870 if (nItr2 > 10) break;
871 } // end while
872 }
873 } else {
874 keepCleaning = true;
875 for (const TrackletSegment &refitseg : refitsegs) CleanSegs.push_back(refitseg);
876 }
877 }
878 // if there are no extra MDT hits, keep only the first segment as unique
879 else
880 CleanSegs.push_back(*it);
881 }
882 } // end it loop
883 if (keepCleaning) {
884 segs = CleanSegs;
885 CleanSegs.clear();
886 }
887 if (nItr > 10) break;
888 } // end while
889
890 return CleanSegs;
891 }
892
893 //** ----------------------------------------------------------------------------------------------------------------- **//
894
895 bool MSVertexTrackletTool::DeltabCalc(const TrackletSegment& ML1seg, const TrackletSegment& ML2seg) const {
896 double ChMid = (ML1seg.getChMidPoint() + ML2seg.getChMidPoint()) / 2.0;
897 // Calculate the Delta b (see http://inspirehep.net/record/1266438)
898 double mid1(100), mid2(1000);
899 double deltab(100);
900 if (m_idHelperSvc->mdtIdHelper().isBarrel(ML1seg.getIdentifier())) {
901 // delta b in the barrel
902 mid1 = (ChMid - ML1seg.globalPosition().perp()) / std::tan(ML1seg.alpha()) + ML1seg.globalPosition().z();
903 mid2 = (ChMid - ML2seg.globalPosition().perp()) / std::tan(ML2seg.alpha()) + ML2seg.globalPosition().z();
904 double r01 = ML1seg.globalPosition().perp() - ML1seg.globalPosition().z() * std::tan(ML1seg.alpha());
905 double r02 = ML2seg.globalPosition().perp() - ML2seg.globalPosition().z() * std::tan(ML2seg.alpha());
906 deltab = (mid2 * std::tan(ML1seg.alpha()) - ChMid + r01) / (std::hypot(1,std::tan(ML1seg.alpha())));
907 double deltab2 = (mid1 * std::tan(ML2seg.alpha()) - ChMid + r02) / (std::hypot(1,std::tan(ML2seg.alpha())));
908 if (std::abs(deltab2) < std::abs(deltab)) deltab = deltab2;
909 } else {
910 // delta b in the endcap
911 mid1 = ML1seg.globalPosition().perp() + std::tan(ML1seg.alpha()) * (ChMid - ML1seg.globalPosition().z());
912 mid2 = ML2seg.globalPosition().perp() + std::tan(ML2seg.alpha()) * (ChMid - ML2seg.globalPosition().z());
913 double z01 = ML1seg.globalPosition().z() - ML1seg.globalPosition().perp() / std::tan(ML1seg.alpha());
914 double z02 = ML1seg.globalPosition().z() - ML1seg.globalPosition().perp() / std::tan(ML1seg.alpha());
915 deltab = (mid2 / std::tan(ML1seg.alpha()) - ChMid + z01) / (std::hypot(1,1/std::tan(ML1seg.alpha())));
916 double deltab2 = (mid1 / std::tan(ML2seg.alpha()) - ChMid + z02) / (std::hypot(1,1/std::tan(ML2seg.alpha())));
917 if (std::abs(deltab2) < std::abs(deltab)) deltab = deltab2;
918 }
919
920 // calculate the maximum allowed Delta b based on delta alpha uncertainties and ML spacing
921 double dbmax = 5 * std::abs(ChMid - ML1seg.getChMidPoint()) * std::hypot(ML1seg.alphaError(), ML2seg.alphaError());
922 if (dbmax > m_maxDeltabCut) dbmax = m_maxDeltabCut;
923 return std::abs(deltab) < dbmax;
924 }
925
926 //** ----------------------------------------------------------------------------------------------------------------- **//
927
928 double MSVertexTrackletTool::TrackMomentum(const Identifier trkID, const double deltaAlpha) const {
929 // p = k/delta_alpha
930 bool isBarrel = m_idHelperSvc->mdtIdHelper().isBarrel(trkID);
931 bool isSmall = m_idHelperSvc->mdtIdHelper().isSmall(trkID);
932 int stationRegion = m_idHelperSvc->mdtIdHelper().stationRegion(trkID);
933
934 double dalpha = std::abs(deltaAlpha);
935 double pTot = m_straightTrackletpTot;
936 if (isBarrel){
937 if (stationRegion == 0){
938 if (isSmall) pTot = m_straightTrackletpTot; // default value for BIS
939 else pTot = c_BIL / dalpha;
940 }
941 else if (stationRegion == 2){
942 if (isSmall) pTot = c_BMS / dalpha;
943 else pTot = c_BML / dalpha;
944 }
945 else if (stationRegion == 3){
946 if (isSmall) pTot = m_straightTrackletpTot; // default value for BOS
947 else pTot = c_BOL / dalpha;
948 }
949 }
950
951 if (pTot > m_maxpTot) pTot = m_straightTrackletpTot;
952
953 return pTot;
954 }
955
956 //** ----------------------------------------------------------------------------------------------------------------- **//
957
959 // uncertainty on 1/p
960 const Identifier trkID = ml1.getIdentifier();
961 bool isBarrel = m_idHelperSvc->mdtIdHelper().isBarrel(trkID);
962 bool isSmall = m_idHelperSvc->mdtIdHelper().isSmall(trkID);
963 int stationRegion = m_idHelperSvc->mdtIdHelper().stationRegion(trkID);
964
965 double dalpha = std::hypot(ml1.alphaError(), ml2.alphaError());
966 double pErr = dalpha / c_BML;
967 if (isBarrel){
968 if (stationRegion == 0){
969 if (isSmall) pErr = dalpha / c_BML; // default value for BIS
970 else pErr = dalpha / c_BIL;
971 }
972 else if (stationRegion == 2){
973 if (isSmall) pErr = dalpha / c_BMS;
974 else pErr = dalpha / c_BML;
975 }
976 else if (stationRegion == 3){
977 if (isSmall) pErr = dalpha / c_BML; // default value for BOS
978 else pErr = dalpha / c_BOL;
979 }
980 }
981
982 return pErr;
983 }
984
985 //** ----------------------------------------------------------------------------------------------------------------- **//
986
988 // uncertainty in 1/p
989 const Identifier trkID = ml1.getIdentifier();
990 bool isBarrel = m_idHelperSvc->mdtIdHelper().isBarrel(trkID);
991 bool isSmall = m_idHelperSvc->mdtIdHelper().isSmall(trkID);
992 int stationRegion = m_idHelperSvc->mdtIdHelper().stationRegion(trkID);
993
994 double dalpha = std::abs(ml1.alphaError());
995 double pErr = dalpha / c_BML;
996 if (isBarrel){
997 if (stationRegion == 0){
998 if (isSmall) pErr = dalpha / c_BML; // default value for BIS
999 else pErr = dalpha / c_BIL;
1000 }
1001 else if (stationRegion == 2){
1002 if (isSmall) pErr = dalpha / c_BMS;
1003 else pErr = dalpha / c_BML;
1004 }
1005 else if (stationRegion == 3){
1006 if (isSmall) pErr = dalpha / c_BML; // default value for BOS
1007 else pErr = dalpha / c_BOL;
1008 }
1009 }
1010
1011 return pErr;
1012 }
1013
1014 //** ----------------------------------------------------------------------------------------------------------------- **//
1015
1016 std::vector<Tracklet> MSVertexTrackletTool::ResolveAmbiguousTracklets(std::vector<Tracklet>& tracks) const {
1017 ATH_MSG_DEBUG("In ResolveAmbiguousTracks");
1018 // considering only tracklets with the number of associated hits
1019 // being more than 3/4 the number of layers in the MS chamber
1020
1022 std::vector<Tracklet> myTracks = tracks;
1023 tracks.clear();
1024 for (const Tracklet &track : myTracks) {
1025 Identifier id1 = track.getML1seg().mdtHitsOnTrack().at(0)->identify();
1026 Identifier id2 = track.getML2seg().mdtHitsOnTrack().at(0)->identify();
1027 int nLayerML1 = m_idHelperSvc->mdtIdHelper().tubeLayerMax(id1);
1028 int nLayerML2 = m_idHelperSvc->mdtIdHelper().tubeLayerMax(id2);
1029 double ratio = (double)(track.mdtHitsOnTrack().size()) / (nLayerML1 + nLayerML2);
1030 if (ratio > 0.75) tracks.push_back(track);
1031 }
1032 }
1033
1034 std::vector<Tracklet> UniqueTracks;
1035 std::vector<unsigned int> AmbigTrks; // indices of ambigious tracklets
1036 for (unsigned int tk1 = 0; tk1 < tracks.size(); ++tk1) {
1037 int nShared = 0;
1038 // check if any Ambiguity has been broken
1039 bool isResolved = false;
1040 for (unsigned int AmbigTrksIdx : AmbigTrks) {
1041 if (tk1 == AmbigTrksIdx) {
1042 isResolved = true;
1043 break;
1044 }
1045 }
1046 if (isResolved) continue;
1047 std::vector<Tracklet> AmbigTracks;
1048 AmbigTracks.push_back(tracks.at(tk1));
1049 // get a point on the track
1050 double Trk1ML1R = tracks.at(tk1).getML1seg().globalPosition().perp();
1051 double Trk1ML1Z = tracks.at(tk1).getML1seg().globalPosition().z();
1052 double Trk1ML2R = tracks.at(tk1).getML2seg().globalPosition().perp();
1053 double Trk1ML2Z = tracks.at(tk1).getML2seg().globalPosition().z();
1054
1055 Identifier tk1ID = tracks.at(tk1).muonIdentifier();
1056 bool tk1_isBarrel = m_idHelperSvc->mdtIdHelper().isBarrel(tk1ID);
1057 bool tk1_isEndcap = m_idHelperSvc->mdtIdHelper().isEndcap(tk1ID);
1058
1059 // loop over the rest of the tracks and find any amibuities
1060 for (unsigned int tk2 = (tk1 + 1); tk2 < tracks.size(); ++tk2) {
1061 if (tracks.at(tk1).mdtChamber() == tracks.at(tk2).mdtChamber() && tracks.at(tk1).mdtChPhi() == tracks.at(tk2).mdtChPhi() &&
1062 (tracks.at(tk1).mdtChEta()) * (tracks.at(tk2).mdtChEta()) > 0) {
1063 // check if any Ambiguity has been broken
1064 for (unsigned int AmbigTrksIdx : AmbigTrks) {
1065 if (tk2 == AmbigTrksIdx) {
1066 isResolved = true;
1067 break;
1068 }
1069 }
1070 if (isResolved) continue;
1071 // get a point on the track
1072 double Trk2ML1R = tracks.at(tk2).getML1seg().globalPosition().perp();
1073 double Trk2ML1Z = tracks.at(tk2).getML1seg().globalPosition().z();
1074 double Trk2ML2R = tracks.at(tk2).getML2seg().globalPosition().perp();
1075 double Trk2ML2Z = tracks.at(tk2).getML2seg().globalPosition().z();
1076
1077 // find the distance between the tracks
1078 double DistML1(1000), DistML2(1000);
1079 if (tk1_isBarrel) {
1080 DistML1 = std::abs(Trk1ML1Z - Trk2ML1Z);
1081 DistML2 = std::abs(Trk1ML2Z - Trk2ML2Z);
1082 } else if (tk1_isEndcap) {
1083 DistML1 = std::abs(Trk1ML1R - Trk2ML1R);
1084 DistML2 = std::abs(Trk1ML2R - Trk2ML2R);
1085 }
1086 if (DistML1 < 40 || DistML2 < 40) {
1087 // find how many MDTs the tracks share
1088 std::vector<const Muon::MdtPrepData*> mdts1 = tracks.at(tk1).mdtHitsOnTrack();
1089 std::vector<const Muon::MdtPrepData*> mdts2 = tracks.at(tk2).mdtHitsOnTrack();
1090 nShared = 0;
1091 for (const Muon::MdtPrepData *mdt1 : mdts1) {
1092 for (const Muon::MdtPrepData *mdt2 : mdts2) {
1093 if (mdt1->identify() == mdt2->identify()) {
1094 ++nShared;
1095 break;
1096 }
1097 }
1098 }
1099
1100 if (nShared <= 1) continue; // if the tracks share only 1 hits move to next track
1101 // store the track as ambiguous
1102 AmbigTracks.push_back(tracks.at(tk2));
1103 AmbigTrks.push_back(tk2);
1104 }
1105 } // end chamber match
1106 } // end tk2 loop
1107
1108 if (AmbigTracks.size() == 1) {
1109 UniqueTracks.push_back(tracks.at(tk1));
1110 continue;
1111 }
1112 // Deal with any ambiguities
1113 // Barrel tracks
1114 if (tk1_isBarrel) {
1115 bool hasMomentum = tracks.at(tk1).charge() != 0;
1116 double aveX(0), aveY(0), aveZ(0), aveAlpha(0);
1117 double aveP(0), nAmbigP(0), TrkCharge(tracks.at(tk1).charge());
1118 bool allSameSign(true);
1119
1120 for (const Tracklet &AmbigTrack : AmbigTracks) {
1121 if (!hasMomentum) {
1122 aveX += AmbigTrack.globalPosition().x();
1123 aveY += AmbigTrack.globalPosition().y();
1124 aveZ += AmbigTrack.globalPosition().z();
1125 aveAlpha += AmbigTrack.getML1seg().alpha();
1126 } else {
1127 // check the charge is the same
1128 if (std::abs(AmbigTrack.charge() - TrkCharge) > 0.1) allSameSign = false;
1129 // find the average momentum
1130 aveP += AmbigTrack.momentum().mag();
1131 ++nAmbigP;
1132 aveAlpha += AmbigTrack.alpha();
1133 aveX += AmbigTrack.globalPosition().x();
1134 aveY += AmbigTrack.globalPosition().y();
1135 aveZ += AmbigTrack.globalPosition().z();
1136 }
1137 } // end loop on ambiguous tracks
1138 if (!hasMomentum) {
1139 aveX = aveX / (double)AmbigTracks.size();
1140 aveY = aveY / (double)AmbigTracks.size();
1141 aveZ = aveZ / (double)AmbigTracks.size();
1142 Amg::Vector3D gpos(aveX, aveY, aveZ);
1143 aveAlpha = aveAlpha / (double)AmbigTracks.size();
1144 double alphaErr = tracks.at(tk1).getML1seg().alphaError();
1145 double rErr = tracks.at(tk1).getML1seg().rError();
1146 double zErr = tracks.at(tk1).getML1seg().zError();
1147
1148 TrackletSegment aveSegML1(m_idHelperSvc.get(), tracks.at(tk1).getML1seg().mdtHitsOnTrack(), gpos, aveAlpha, alphaErr, rErr, zErr, 0);
1149 double pT = m_maxpTot * std::sin(aveSegML1.alpha());
1150 double pz = m_maxpTot * std::cos(aveSegML1.alpha());
1151 Amg::Vector3D momentum(pT * std::cos(aveSegML1.globalPosition().phi()),
1152 pT * std::sin(aveSegML1.globalPosition().phi()),
1153 pz);
1154 AmgSymMatrix(5) matrix;
1155 matrix.setIdentity();
1156 matrix(0, 0) = std::pow(tracks.at(tk1).getML1seg().rError(),2); // delta R
1157 matrix(1, 1) = std::pow(tracks.at(tk1).getML1seg().zError(),2); // delta z
1158 matrix(2, 2) = std::pow(0.0000001,2); // delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber)
1159 matrix(3, 3) = std::pow(tracks.at(tk1).getML1seg().alphaError(),2); // delta theta
1160 matrix(4, 4) = std::pow(m_straightTrackletInvPerr,2); // delta 1/p
1161 Tracklet aveTrack(aveSegML1, momentum, matrix, 0);
1162 UniqueTracks.push_back(aveTrack);
1163 } else if (allSameSign) {
1164 if (nAmbigP == 0)[[unlikely]]{
1165 ATH_MSG_ERROR("nAmbigP == 0");
1166 continue;
1167 }
1168 aveP = aveP / nAmbigP;
1169 double pT = aveP * std::sin(tracks.at(tk1).getML1seg().alpha());
1170 double pz = aveP * std::cos(tracks.at(tk1).getML1seg().alpha());
1171 Amg::Vector3D momentum(pT * std::cos(tracks.at(tk1).globalPosition().phi()),
1172 pT * std::sin(tracks.at(tk1).globalPosition().phi()),
1173 pz);
1174 Tracklet MyTrack = tracks.at(tk1);
1175 MyTrack.momentum(momentum);
1176 MyTrack.charge(tracks.at(tk1).charge());
1177 UniqueTracks.push_back(MyTrack);
1178 } else {
1179 aveX = aveX / (double)AmbigTracks.size();
1180 aveY = aveY / (double)AmbigTracks.size();
1181 aveZ = aveZ / (double)AmbigTracks.size();
1182 Amg::Vector3D gpos(aveX, aveY, aveZ);
1183 aveAlpha = aveAlpha / (double)AmbigTracks.size();
1184 double alphaErr = tracks.at(tk1).getML1seg().alphaError();
1185 double rErr = tracks.at(tk1).getML1seg().rError();
1186 double zErr = tracks.at(tk1).getML1seg().zError();
1187
1188 TrackletSegment aveSegML1(m_idHelperSvc.get(), tracks.at(tk1).getML1seg().mdtHitsOnTrack(), gpos, aveAlpha, alphaErr, rErr, zErr, 0);
1189 double pT = m_maxpTot * std::sin(aveSegML1.alpha());
1190 double pz = m_maxpTot * std::cos(aveSegML1.alpha());
1191 Amg::Vector3D momentum(pT * std::cos(aveSegML1.globalPosition().phi()),
1192 pT * std::sin(aveSegML1.globalPosition().phi()),
1193 pz);
1194 AmgSymMatrix(5) matrix;
1195 matrix.setIdentity();
1196 matrix(0, 0) = std::pow(tracks.at(tk1).getML1seg().rError(),2); // delta R
1197 matrix(1, 1) = std::pow(tracks.at(tk1).getML1seg().zError(),2); // delta z
1198 matrix(2, 2) = std::pow(0.0000001,2); // delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber)
1199 matrix(3, 3) = std::pow(tracks.at(tk1).getML1seg().alphaError(),2); // delta theta
1200 matrix(4, 4) = std::pow(m_straightTrackletInvPerr,2); // delta 1/p
1201 Tracklet aveTrack(aveSegML1, momentum, matrix, 0);
1202 UniqueTracks.push_back(aveTrack);
1203 }
1204 } // end barrel Tracks
1205
1206 // Endcap tracks
1207 else if (tk1_isEndcap) {
1208 std::vector<const Muon::MdtPrepData*> AllMdts;
1209 for (Tracklet const &AmbigTrack : AmbigTracks) {
1210 std::vector<const Muon::MdtPrepData*> mdts = AmbigTrack.mdtHitsOnTrack();
1211 std::vector<const Muon::MdtPrepData*> tmpAllMdt = AllMdts;
1212 for (const Muon::MdtPrepData *mdt : mdts) {
1213 bool isNewHit = true;
1214 for (const Muon::MdtPrepData *tmpmdt : tmpAllMdt) {
1215 if (mdt->identify() == tmpmdt->identify()) {
1216 isNewHit = false;
1217 break;
1218 }
1219 }
1220 if (isNewHit) AllMdts.push_back(mdt);
1221 } // end loop on mdts
1222 } // end loop on ambiguous tracks
1223
1224 std::vector<TrackletSegment> MyECsegs = TrackletSegmentFitter(AllMdts);
1225 if (!MyECsegs.empty()) {
1226 TrackletSegment ECseg = MyECsegs.at(0);
1227 ECseg.clearMdt();
1228 double pT = m_maxpTot * std::sin(ECseg.alpha());
1229 double pz = m_maxpTot * std::cos(ECseg.alpha());
1230 Amg::Vector3D momentum(pT * std::cos(ECseg.globalPosition().phi()),
1231 pT * std::sin(ECseg.globalPosition().phi()),
1232 pz);
1233 AmgSymMatrix(5) matrix;
1234 matrix.setIdentity();
1235 matrix(0, 0) = std::pow(ECseg.rError(),2); // delta R
1236 matrix(1, 1) = std::pow(ECseg.zError(),2); // delta z
1237 matrix(2, 2) = std::pow(0.0000001,2); // delta phi (~0 because we explicitly rotate all tracks into the middle of the chamber)
1238 matrix(3, 3) = std::pow(ECseg.alphaError(),2); // delta theta
1239 matrix(4, 4) = std::pow(m_straightTrackletInvPerr,2); // delta 1/p (endcap tracks are straight lines with no momentum that we can measure ...)
1240 Tracklet MyCombTrack(MyECsegs.at(0), ECseg, momentum, matrix, 0);
1241 UniqueTracks.push_back(MyCombTrack);
1242 } else
1243 UniqueTracks.push_back(tracks.at(tk1));
1244 } // end endcap tracks
1245
1246 } // end loop on tracks -- tk1
1247
1248 return UniqueTracks;
1249 }
1250
1251} // namespace Muon
#define M_PI
Scalar perp() const
perp method - perpendicular length
Scalar phi() const
phi method
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
double charge(const T &p)
Definition AtlasPID.h:1003
#define AmgSymMatrix(dim)
std::pair< std::vector< unsigned int >, bool > res
static Double_t sz
size_t size() const
Number of registered mappings.
#define z
static const Attributes_t empty
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
bool msgLvl(const MSG::Level lvl) const
MsgStream & msg() const
MSVertexTrackletTool(const std::string &type, const std::string &name, const IInterface *parent)
int SortMDThits(std::vector< std::vector< const Muon::MdtPrepData * > > &SortedMdt, const EventContext &ctx) const
Gaudi::Property< bool > m_tightTrackletRequirement
static double SeedResiduals(const std::vector< const Muon::MdtPrepData * > &mdts, double slope, double inter)
Gaudi::Property< double > m_straightTrackletpTot
SG::WriteHandleKey< xAOD::TrackParticleContainer > m_TPContainer
double TrackMomentumError(const TrackletSegment &ml1, const TrackletSegment &ml2) const
static void convertToTrackParticles(std::vector< Tracklet > &tracklets, SG::WriteHandle< xAOD::TrackParticleContainer > &container)
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
SG::ReadHandleKey< Muon::MdtPrepDataContainer > m_mdtTESKey
Gaudi::Property< double > m_maxDeltabCut
std::vector< std::pair< double, double > > SegSeeds(const std::vector< const Muon::MdtPrepData * > &mdts) const
bool DeltabCalc(const TrackletSegment &ML1seg, const TrackletSegment &ML2seg) const
double TrackMomentum(const Identifier trkID, const double deltaAlpha) const
Gaudi::Property< double > m_minSegFinderChi2
bool IgnoreMDTChamber(const Muon::MdtPrepData *mdtHit) const
std::vector< TrackletSegment > TrackletSegmentFitter(const std::vector< const Muon::MdtPrepData * > &mdts) const
Gaudi::Property< double > m_d13_max
std::vector< Tracklet > ResolveAmbiguousTracklets(std::vector< Tracklet > &tracks) const
std::vector< TrackletSegment > TrackletSegmentFitterCore(const std::vector< const Muon::MdtPrepData * > &mdts, const std::vector< std::pair< double, double > > &SeedParams) const
Gaudi::Property< double > m_errorCutOff
void addMDTHits(std::vector< const Muon::MdtPrepData * > &hits, std::vector< std::vector< const Muon::MdtPrepData * > > &SortedMdt) const
Gaudi::Property< double > m_SeedResidual
Gaudi::Property< double > m_minpTot
Gaudi::Property< double > m_straightTrackletInvPerr
StatusCode findTracklets(std::vector< Tracklet > &tracklets, const EventContext &ctx) const override
virtual StatusCode initialize() override
std::vector< TrackletSegment > CleanSegments(const std::vector< TrackletSegment > &segs) const
Gaudi::Property< double > m_EndcapDeltaAlphaCut
Gaudi::Property< double > m_BarrelDeltaAlphaCut
Gaudi::Property< double > m_d12_max
Gaudi::Property< double > m_maxpTot
Class to represent measurements from the Monitored Drift Tubes.
Definition MdtPrepData.h:33
virtual bool isValid() override final
Can the handle be successfully dereferenced?
New segment class for single ML segments.
double alpha() const
const Identifier getIdentifier() const
const Amg::Vector3D & globalPosition() const
double alphaError() const
double getChMidPoint() const
double rError() const
double zError() const
void momentum(const Amg::Vector3D &p)
Definition Tracklet.cxx:40
void charge(double charge)
Definition Tracklet.cxx:41
Class describing the Line to which the Perigee refers to.
Identifier identify() const
return the identifier
void setFitQuality(float chiSquared, float numberDoF)
Set the 'Fit Quality' information.
void setDefiningParameters(float d0, float z0, float phi0, float theta, float qOverP)
Set the defining parameters.
void setTrackProperties(const TrackProperties properties)
Methods setting the TrackProperties.
void setDefiningParametersCovMatrixVec(const std::vector< float > &cov)
double chi2(TH1 *h0, TH1 *h1)
std::string base
Definition hcg.cxx:83
void compress(const AmgSymMatrix(N) &covMatrix, std::vector< float > &vec)
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::Matrix< double, 3, 1 > Vector3D
bool isSmall(const ChIndex index)
Returns true if the chamber index is in a small sector.
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
const std::string & stName(StIndex index)
convert StIndex into a string
NRpcCablingAlg reads raw condition data and writes derived condition data to the condition store.
constexpr double c_BML
constexpr double c_BIL
constexpr double c_BOL
constexpr double c_BMS
@ MdtStatusDriftTime
The tube produced a vaild measurement.
MuonPrepDataCollection< MdtPrepData > MdtPrepDataCollection
@ locR
Definition ParamDefs.h:44
@ phi0
Definition ParamDefs.h:65
@ theta
Definition ParamDefs.h:66
@ qOverP
perigee
Definition ParamDefs.h:67
@ d0
Definition ParamDefs.h:63
@ z0
Definition ParamDefs.h:64
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
@ LowPtTrack
A LowPt track.
#define unlikely(x)
Tell the compiler to optimize assuming that FP may trap.
#define CXXUTILS_TRAPPING_FP
Definition trapping_fp.h:24