ATLAS Offline Software
Loading...
Searching...
No Matches
InDetSecVtxTruthMatchTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3*/
6
7#include "TVector3.h"
8#include <cmath>
9#include "xAODMuon/Muon.h"
14
15using namespace InDetSecVtxTruthMatchUtils;
16
18
20 ATH_MSG_INFO("Initializing InDetSecVtxTruthMatchTool");
21
22 // Retrieve the TrackTruthOriginTool
24 if (m_doMuSA) {
25 ATH_MSG_INFO("MuSA mode enabled. Muon container: " << m_muonContainerName);
26 if (!m_muonFallbackContainerName.value().empty()) {
27 ATH_MSG_INFO("MuSA fallback muon container: " << m_muonFallbackContainerName);
28 }
29 }
30
31 return StatusCode::SUCCESS;
32}
33
34namespace {
35//Helper methods for this file only
36
37//In the vector of match info, find the element corresponding to link and return its index; create a new one if necessary
38size_t indexOfMatchInfo( std::vector<VertexTruthMatchInfo> & matches, const ElementLink<xAOD::TruthVertexContainer> & link ) {
39 for ( size_t i = 0; i < matches.size(); ++i ) {
40 if ( link.key() == std::get<0>(matches[i]).key() && link.index() == std::get<0>(matches[i]).index() )
41 return i;
42 }
43 // This is the first time we've seen this truth vertex, so make a new entry
44 matches.emplace_back( link, 0., 0. );
45 return matches.size() - 1;
46}
47
48}
49
50StatusCode InDetSecVtxTruthMatchTool::matchVertices( std::vector<const xAOD::Vertex*> recoVerticesToMatch,
51 std::vector<const xAOD::TruthVertex*> truthVerticesToMatch,
52 const xAOD::TrackParticleContainer* trackParticles) {
53
54 ATH_MSG_DEBUG("Start vertex matching");
55
56 const xAOD::MuonContainer* muonContainer = nullptr;
57 if (m_doMuSA) {
58 if (evtStore()->retrieve(muonContainer, m_muonContainerName).isFailure()) {
59 muonContainer = nullptr;
60 if (!m_muonFallbackContainerName.value().empty()) {
61 ATH_MSG_WARNING("Failed to retrieve muon container '" << m_muonContainerName
62 << "'. Attempting fallback container '" << m_muonFallbackContainerName << "'.");
63 if (evtStore()->retrieve(muonContainer, m_muonFallbackContainerName).isFailure()) {
64 ATH_MSG_ERROR("Failed to retrieve fallback muon container: " << m_muonFallbackContainerName);
65 return StatusCode::FAILURE;
66 }
67 } else {
68 ATH_MSG_ERROR("Failed to retrieve muon container: " << m_muonContainerName);
69 return StatusCode::FAILURE;
70 }
71 }
72 }
73
74 //setup decorators for truth matching info
75 static const xAOD::Vertex::Decorator<std::vector<VertexTruthMatchInfo> > matchInfoDecor("truthVertexMatchingInfos");
76 static const xAOD::Vertex::Decorator<int> recoMatchTypeDecor("vertexMatchType");
77 static const xAOD::Vertex::Decorator<std::vector<ElementLink<xAOD::VertexContainer> > > splitPartnerDecor("splitPartners");
78 //optional for SM origin matching
79 static const xAOD::Vertex::Decorator<int> smOriginDecor("vertexMatchOriginType");
80
81 const xAOD::Vertex::Decorator<float> fakeScoreDecor("fakeScore");
82 const xAOD::Vertex::Decorator<float> otherScoreDecor("otherScore");
83
84 //setup accessors
85 // can switch to built in method in xAOD::Vertex once don't have to deal with changing names anymore
87 xAOD::Vertex::ConstAccessor<std::vector<float> > weightAcc("trackWeights");
89 xAOD::TrackParticle::ConstAccessor<float> trk_truthProbAcc("truthMatchProbability");
90
91 ATH_MSG_DEBUG("Starting Loop on Vertices");
92
93 //=============================================================================
94 //First loop over vertices: get tracks, then TruthParticles, and store relative
95 //weights from each TruthVertex
96 //=============================================================================
97 for ( const xAOD::Vertex* vtx : recoVerticesToMatch ) {
98
99 //create the vector we will add as matching info decoration later
100 std::vector<VertexTruthMatchInfo> matchinfo;
101
102 const xAOD::Vertex::TrackParticleLinks_t& trackParticles = trkAcc( *vtx );
103 size_t ntracks = trackParticles.size();
104 const std::vector<float> & trkWeights = weightAcc( *vtx );
105
106 xAOD::Vertex::TrackParticleLinks_t trkMuSATrkParts; // For MuSA mode, we will populate this with MuSA track particles
107
108 // Create a local variable for track particles to use in the rest of the function
109 const xAOD::Vertex::TrackParticleLinks_t& trkParts = m_doMuSA ? trkMuSATrkParts : trackParticles;
110
111 if ( m_doMuSA ) {
112 // MuSA also creates new "MuSA Track" collection which does not have truth particle links
113 // instead, we need to get the associated MS TrackParticle from the "MuSATrk_MSTPLink" decorations on the MuSA Track
114 // and then get the truth particle link from there
115 const SG::AuxElement::Accessor<ElementLink<xAOD::TrackParticleContainer>> acc_MSTPLink("MuSATrk_MSTPLink");
116 // populate the MuSA track particle vector
117 trkMuSATrkParts.reserve(ntracks);
118 for (const auto& trkLink : trackParticles) {
119 if (!trkLink.isValid()) continue;
120 const xAOD::TrackParticle& trackParticle = **trkLink;
121 if (acc_MSTPLink.isAvailable(trackParticle)) {
122 const ElementLink<xAOD::TrackParticleContainer>& trkMuSATrkLink = acc_MSTPLink(trackParticle);
123 if (trkMuSATrkLink.isValid()) {
124 trkMuSATrkParts.push_back(trkMuSATrkLink);
125 } else {
126 ATH_MSG_WARNING("MS track particle link is not valid");
127 }
128 } else {
129 ATH_MSG_WARNING("MuSATrk_MSTPLink decoration is not available on track particle");
130 }
131 }
132 }
133
134 //if don't have track particles
135 if ((!trkAcc.isAvailable(*vtx) || !weightAcc.isAvailable(*vtx)) && !m_doMuSA) {
136 ATH_MSG_WARNING("trackParticles or trackWeights not available, vertex is missing info");
137 continue;
138 }
139 if ( trkWeights.size() != ntracks ) {
140 ATH_MSG_WARNING("Vertex without same number of tracks and trackWeights, vertex is missing info");
141 continue;
142 }
143
144 ATH_MSG_DEBUG("Matching new vertex at (" << vtx->x() << ", " << vtx->y() << ", " << vtx->z() << ")" << " with " << ntracks << " tracks, at index: " << vtx->index());
145
146 float totalWeight = 0.;
147 float totalPt = 0;
148 float otherPt = 0;
149 float fakePt = 0;
150
151 // Add for SM origin tracking
152 int combinedSMOrigin = 0;
153
154 //loop over the tracks in the vertex
155 for ( size_t t = 0; t < ntracks; ++t ) {
156
157 ATH_MSG_DEBUG("Checking track number " << t);
158
159 // First check if the original track particle link is valid
160 if (!trackParticles[t].isValid()) {
161 ATH_MSG_DEBUG("Original track " << t << " is bad!");
162 continue;
163 }
164
165 // Then check if we have a valid track after potential MuSA processing
166 if (t >= trkParts.size() || !trkParts[t].isValid()) {
167 ATH_MSG_DEBUG("Track " << t << " is invalid or out of bounds in trkParts!");
168 continue;
169 }
170 const xAOD::TrackParticle trk = **trkParts[t];
171
172 // Add to total weight
173 if (m_doMuSA) {
174 totalWeight += 1.0; // Add dummy weight of 1 for each track with MuSA
175 } else {
176 totalWeight += trkWeights[t];
177 }
178
179 // Safely get the track pt
180 float trkPt = 0;
181 try {
182 trkPt = trk.pt();
183 totalPt += trkPt;
184 } catch (const std::exception& e) {
185 ATH_MSG_WARNING("Exception when accessing track pt: " << e.what());
186 continue;
187 }
188
189 // get the linked truth particle
190 if (!trk_truthPartAcc.isAvailable(trk)) {
191 ATH_MSG_DEBUG("The truth particle link decoration isn't available.");
192 continue;
193 }
194 const ElementLink<xAOD::TruthParticleContainer> & truthPartLink = trk_truthPartAcc(trk);
195 float prob = 0.0;
196 if (m_doMuSA) {
197 // For MuSA mode, assign a dummy probability of 1.0
198 prob = 1.0;
199 ATH_MSG_DEBUG("MuSA mode: using dummy truth prob of 1.0");
200 } else {
201 // For regular mode, use the decoration
202 prob = trk_truthProbAcc(trk);
203 ATH_MSG_DEBUG("Truth prob: " << prob);
204 }
205
206 // check the truth particle origin
207 if (truthPartLink.isValid() && prob > m_trkMatchProb) {
208 const xAOD::TruthParticle & truthPart = **truthPartLink;
209
210 const int ancestorVertexUniqueID = checkProduction(truthPart, truthVerticesToMatch);
211 // optional SM origin classification
212 if (m_doSMOrigin) {
213 const int smOrigin = checkSMProduction(truthPart);
214 combinedSMOrigin |= smOrigin;
215
216 // Check if this track is from LLP signal
217 if (ancestorVertexUniqueID != HepMC::INVALID_VERTEX_ID) {
218 combinedSMOrigin |= (0x1 << InDetSecVtxTruthMatchUtils::Signal);
219 }
220 }
221
222 //check if the truth particle is "good"
223 if (ancestorVertexUniqueID != HepMC::INVALID_VERTEX_ID) {
224 //track in vertex is linked to LLP descendant
225 //create link to truth vertex and add to matchInfo
226 auto it = std::find_if(truthVerticesToMatch.begin(), truthVerticesToMatch.end(),
227 [&](const auto& ele){ return HepMC::uniqueID(ele) == ancestorVertexUniqueID;} );
228
229 if (it == truthVerticesToMatch.end()) {
230 ATH_MSG_WARNING("Truth vertex with unique ID " << ancestorVertexUniqueID << " not found!");
231 } else {
233 elLink.setElement(*it);
234 elLink.setStorableObject(*dynamic_cast<const xAOD::TruthVertexContainer*>((*it)->container()));
235 size_t matchIdx = indexOfMatchInfo(matchinfo, elLink);
236
237 if (m_doMuSA) {
238 std::get<1>(matchinfo[matchIdx]) += 1.0; // Add dummy weight of 1 for MuSA
239 } else {
240 std::get<1>(matchinfo[matchIdx]) += trkWeights[t];
241 }
242 std::get<2>(matchinfo[matchIdx]) += trk.pt();
243 }
244 } else {
245 //truth particle failed cuts
246 ATH_MSG_DEBUG("Truth particle not from LLP decay.");
247 otherPt += trk.pt();
248 }
249 } else {
250 //not valid or low matching probability
251 ATH_MSG_DEBUG("Invalid or low prob truth link!");
252 fakePt += trk.pt();
253 // Mark as fake for SM origin tracking
254 if (m_doSMOrigin) {
255 combinedSMOrigin |= (0x1 << InDetSecVtxTruthMatchUtils::FakeOrigin);
256 }
257 }
258 }//end loop over tracks in vertex
259
260 // normalize by total weight and pT
261 std::for_each( matchinfo.begin(), matchinfo.end(), [&](VertexTruthMatchInfo& link)
262 {
263 std::get<1>(link) /= totalWeight;
264 std::get<2>(link) /= totalPt;
265 });
266
267 float fakeScore = fakePt/totalPt;
268 float otherScore = otherPt/totalPt;
269
270 matchInfoDecor ( *vtx ) = matchinfo;
271 fakeScoreDecor ( *vtx ) = fakeScore;
272 otherScoreDecor( *vtx ) = otherScore;
273
274 // Decorate with SM origin if enabled
275 if (m_doSMOrigin) {
276 smOriginDecor( *vtx ) = combinedSMOrigin;
277 }
278 }
279
280 //After first loop, all vertices have been decorated with their vector of match info (link to TruthVertex paired with weight)
281 //now we want to use that information from the whole collection to assign types
282 //keep track of whether a type is assigned
283 //useful since looking for splits involves a double loop, and then setting types ahead in the collection
284 std::vector<bool> assignedType( recoVerticesToMatch.size(), false );
285 static const xAOD::TruthVertex::Decorator<bool> isMatched("matchedToRecoVertex");
286 static const xAOD::TruthVertex::Decorator<bool> isSplit("vertexSplit");
287
288 for ( size_t i = 0; i < recoVerticesToMatch.size(); ++i ) {
289
290 int recoVertexMatchType = 0;
291
292 if ( assignedType[i] ) {
293 ATH_MSG_DEBUG("Vertex already defined as split.");
294 continue; // make sure we don't reclassify vertices already found in the split loop below
295 }
296
297 std::vector<VertexTruthMatchInfo> & info = matchInfoDecor( *recoVerticesToMatch[i] );
298 float fakeScore = fakeScoreDecor( *recoVerticesToMatch[i] );
299
300 if(fakeScore > m_vxMatchWeight) {
301 ATH_MSG_DEBUG("Vertex is fake.");
302 recoVertexMatchType = recoVertexMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Fake);
303 } else if (info.size() == 1) {
304 if(std::get<2>(info[0]) > m_vxMatchWeight ) { // one truth matched vertex, sufficient weight
305 ATH_MSG_DEBUG("One true decay vertices matched with sufficient weight. Vertex is matched.");
306 recoVertexMatchType = recoVertexMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Matched);
307 isMatched(**std::get<0>(info[0])) = true;
308 }
309 else {
310 ATH_MSG_DEBUG("One true decay vertices matched with insufficient weight. Vertex is other.");
311 recoVertexMatchType = recoVertexMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Other);
312 }
313 } else if (info.size() >= 2 ) { // more than one true deacy vertices matched
314 ATH_MSG_DEBUG("Multiple true decay vertices matched. Vertex is merged.");
315 recoVertexMatchType = recoVertexMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Merged);
316 std::for_each( info.begin(), info.end(), [](VertexTruthMatchInfo& link)
317 {
318 isMatched(**std::get<0>(link)) = true;
319 });
320 } else { // zero truth matched vertices, but not fake
321 ATH_MSG_DEBUG("Vertex is neither fake nor LLP. Marking as OTHER.");
322 recoVertexMatchType = recoVertexMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Other);
323 }
324
325 recoMatchTypeDecor(*recoVerticesToMatch[i]) = recoVertexMatchType;
326
327 //check for splitting
328 if ( InDetSecVtxTruthMatchUtils::isMatched(recoMatchTypeDecor(*recoVerticesToMatch[i])) ||
329 InDetSecVtxTruthMatchUtils::isMerged(recoMatchTypeDecor(*recoVerticesToMatch[i])) ) {
330 std::vector<size_t> foundSplits;
331 for ( size_t j = i + 1; j < recoVerticesToMatch.size(); ++j ) {
332 std::vector<VertexTruthMatchInfo> & info2 = matchInfoDecor( *recoVerticesToMatch[j] );
333 //check second vertex is not dummy or fake, and that it has same elementlink as first vertex
334 //equality test is in code but doesnt seem to work for ElementLinks that I have?
335 //so i am just checking that the contianer key hash and the index are the same
336 if (recoMatchTypeDecor( *recoVerticesToMatch[j] ) & (0x1 << InDetSecVtxTruthMatchUtils::Fake)) continue;
337 if (!info2.empty() && std::get<0>(info2[0]).isValid() && std::get<0>(info[0]).key() == std::get<0>(info2[0]).key() && std::get<0>(info[0]).index() == std::get<0>(info2[0]).index() ) {
338 //add split links; first between first one found and newest one
340 splitLink_ij.setElement( recoVerticesToMatch[j] );
341 splitLink_ij.setStorableObject( *dynamic_cast<const xAOD::VertexContainer*>(recoVerticesToMatch[j]->container()));
342 splitPartnerDecor( *recoVerticesToMatch[i] ).emplace_back(splitLink_ij);
343
345 splitLink_ji.setElement( recoVerticesToMatch[i] );
346 splitLink_ji.setStorableObject( *dynamic_cast<const xAOD::VertexContainer*>(recoVerticesToMatch[i]->container()));
347 splitPartnerDecor( *recoVerticesToMatch[j] ).emplace_back(splitLink_ji);
348
349 //then between any others we found along the way
350 for ( auto k : foundSplits ) { //k is a size_t in the vector of splits
352 splitLink_kj.setElement( recoVerticesToMatch[j] );
353 splitLink_kj.setStorableObject( *dynamic_cast<const xAOD::VertexContainer*>(recoVerticesToMatch[j]->container()));
354 splitPartnerDecor( *recoVerticesToMatch[k] ).emplace_back(splitLink_kj);
355
357 splitLink_jk.setElement( recoVerticesToMatch[k] );
358 splitLink_jk.setStorableObject( *dynamic_cast<const xAOD::VertexContainer*>(recoVerticesToMatch[k]->container()));
359 splitPartnerDecor( *recoVerticesToMatch[j] ).emplace_back(splitLink_jk);
360 }
361 //then keep track that we found this one
362 foundSplits.push_back(j);
363 recoMatchTypeDecor( *recoVerticesToMatch[i] ) = recoMatchTypeDecor( *recoVerticesToMatch[i] ) | (0x1 << InDetSecVtxTruthMatchUtils::Split);
364 recoMatchTypeDecor( *recoVerticesToMatch[j] ) = recoMatchTypeDecor( *recoVerticesToMatch[j] ) | (0x1 << InDetSecVtxTruthMatchUtils::Split);
365 isSplit(**std::get<0>(info[0])) = true;
366 assignedType[j] = true;
367 } //if the two vertices match to same TruthVertex
368 }//inner loop over vertices
369 } //if matched or merged
370
371 } //outer loop
372
373 // now label truth vertices
374
375 ATH_MSG_DEBUG("Labeling truth vertices");
376
377 static const xAOD::TruthVertex::Decorator<int> truthMatchTypeDecor("truthVertexMatchType");
378
379 for(const xAOD::TruthVertex* truthVtx : truthVerticesToMatch) {
380
381 std::vector<const xAOD::TruthParticle*> reconstructibleParticles;
382 int counter = 0;
383 countReconstructibleDescendentParticles( *truthVtx, reconstructibleParticles, counter );
384
385 // hacky solution for keeping track of particles in the vertex
386 std::vector<int> particleInfo = {0,0,0};
387 std::vector<int> vertexInfo = {0,0,0};
388
389 for(size_t n = 0; n < reconstructibleParticles.size(); n++){
390 ATH_MSG_DEBUG("Checking daughter no. " << n);
391 const xAOD::TruthParticle* outPart = reconstructibleParticles.at(n);
392
393 if (trackParticles){
394 particleInfo = checkParticle(*outPart, trackParticles, muonContainer);
395
396 for(size_t h = 0; h < particleInfo.size(); h++){
397 vertexInfo.at(h) += particleInfo.at(h);
398 }
399 }
400 }
401
402 int truthMatchType = 0;
403 if( vertexInfo.at(0) > 1 &&
404 ((m_doMuSA && (truthVtx->perp() < 8000 && std::abs(truthVtx->z()) < 10000)) ||
405 (!m_doMuSA && truthVtx->perp() < 320 && std::abs(truthVtx->z()) < 1500))){
406 ATH_MSG_DEBUG("Vertex is reconstructable and in " << (m_doMuSA ? "Muon Spectrometer" : "Inner Det") << " region");
407 truthMatchType = truthMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Reconstructable);
408 }
409 if( InDetSecVtxTruthMatchUtils::isReconstructable(truthMatchType) and vertexInfo.at(1) > 1){
410 ATH_MSG_DEBUG("Vertex has at least two tracks");
411 truthMatchType = truthMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Accepted);
412 }
413 if(InDetSecVtxTruthMatchUtils::isAccepted(truthMatchType) and vertexInfo.at(2) > 1){
414 ATH_MSG_DEBUG("Vertex is has at least two tracks passing track selection: " << vertexInfo.at(2));
415 truthMatchType = truthMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Seeded);
416 }
417 if(InDetSecVtxTruthMatchUtils::isSeeded(truthMatchType) and isMatched(*truthVtx)){
418 ATH_MSG_DEBUG("Vertex is matched to a reconstructed secVtx");
419 truthMatchType = truthMatchType | (0x1 << InDetSecVtxTruthMatchUtils::Reconstructed);
420 }
421 if(InDetSecVtxTruthMatchUtils::isSeeded(truthMatchType) and isSplit(*truthVtx)){
422 ATH_MSG_DEBUG("Vertex is matched to multiple secVtx");
423 truthMatchType = truthMatchType | (0x1 << InDetSecVtxTruthMatchUtils::ReconstructedSplit);
424 }
425 truthMatchTypeDecor(*truthVtx) = truthMatchType;
426 }
427 ATH_MSG_DEBUG("Done labeling truth vertices");
428
429 return StatusCode::SUCCESS;
430
431}
432
434 const xAOD::TrackParticleContainer* trkCont,
435 const xAOD::MuonContainer* muonCont) const {
436
439 xAOD::TrackParticle::ConstAccessor<float> trk_truthProbAcc("truthMatchProbability");
440
441 if(truthPart.pt() < m_trkPtCut){
442 ATH_MSG_DEBUG("Insufficient pt to reconstruct the particle");
443 return {0,0,0};
444 }
445 else{
446
447 for(const xAOD::TrackParticle* trkPart : *trkCont){
448 // Handle differently for MuSA vs standard mode
449 if (m_doMuSA) {
450 if (!trk_truthPartAcc.isAvailable(*trkPart)) {
451 ATH_MSG_DEBUG("Truth link not available on MS track");
452 continue;
453 }
454 const ElementLink<xAOD::TruthParticleContainer>& truthLink = trk_truthPartAcc(*trkPart);
455 if (!truthLink.isValid()) {
456 ATH_MSG_DEBUG("Truth link on MS track not valid");
457 continue;
458 }
459 const xAOD::TruthParticle& linkedTruth = **truthLink;
460 if (HepMC::is_same_particle(linkedTruth, truthPart)) {
461 // We found a match between truth particle and MS track!
462 if (!muonCont) {
463 ATH_MSG_DEBUG("Muon container unavailable in MuSA mode; cannot evaluate acceptance criteria.");
464 return {1,0,0};
465 }
466
467 const xAOD::Muon* saMuon = findStandAloneMuon(*trkPart, muonCont);
468 if (!saMuon) {
469 ATH_MSG_DEBUG("Muon track matched but is not associated with a StandAlone muon.");
470 return {1,0,0};
471 }
472
473 float spectrometerFieldIntegral = 0.0f;
474 bool hasSpectrometerField = saMuon->parameter(spectrometerFieldIntegral, xAOD::Muon::spectrometerFieldIntegral);
475 if (!hasSpectrometerField) {
476 ATH_MSG_DEBUG("Standalone muon missing spectrometer field integral parameter; proceeding without field cut.");
477 } else if (spectrometerFieldIntegral < 0.1f) {
478 ATH_MSG_DEBUG("Skipping SA muon with spectrometerFieldIntegral " << spectrometerFieldIntegral << " T*m!");
479 return {1,1,0};
480 }
481
482 const bool passesKinematic = std::abs(trkPart->eta()) < 2.5 && trkPart->pt() < 13000000; // emulates bad egg MSTP rejection used in MuSA algorithm
483 if (passesKinematic) {
484 ATH_MSG_DEBUG("Standalone muon passes MuSA kinematic requirements.");
485 return {1,1,1};
486 }
487
488 ATH_MSG_DEBUG("Standalone muon failed MuSA kinematic requirements.");
489 return {1,1,0};
490 }
491 }
492
493 // Standard mode - check truth matching
494 if (!trk_truthPartAcc.isAvailable(*trkPart)) {
495 ATH_MSG_DEBUG("truthParticleLink not available on track");
496 continue;
497 }
498
499 const ElementLink<xAOD::TruthParticleContainer> & truthPartLink = trk_truthPartAcc(*trkPart);
500
501 // Check if truth match probability is available
502 float matchProb = 0.0;
503 if (trk_truthProbAcc.isAvailable(*trkPart)) {
504 matchProb = trk_truthProbAcc(*trkPart);
505 } else {
506 ATH_MSG_DEBUG("truthMatchProbability not available on track");
507 continue;
508 }
509
510 if(truthPartLink.isValid() && matchProb > m_trkMatchProb) {
511 const xAOD::TruthParticle& tmpPart = **truthPartLink;
512 if( HepMC::is_same_particle(tmpPart,truthPart) ) {
513 if(trackPass.isAvailable( *trkPart )) {
514 if(trackPass( *trkPart )) {
515 ATH_MSG_DEBUG("Particle has a track that passes track selection.");
516 return {1,1,1};
517 } else {
518 ATH_MSG_DEBUG("Particle has a track, but did not pass track selection.");
519 return {1,1,0};
520 }
521 } else {
522 ATH_MSG_DEBUG("Track selection decoration not available, calling the track selected");
523 return {1,1,1};
524 }
525 }
526 }
527 }
528 ATH_MSG_DEBUG("Particle has enough pt.");
529 return {1,0,0};
530
531 }
532 return {0,0,0};
533}
534
536 const xAOD::MuonContainer* muonContainer) const {
537 if (!muonContainer) {
538 return nullptr;
539 }
540
541 for (const xAOD::Muon* muon : *muonContainer) {
542 if (!muon) {
543 continue;
544 }
545 const xAOD::TrackParticle* msTrack = muon->trackParticle(xAOD::Muon::MuonSpectrometerTrackParticle);
546 if (msTrack == &mstp && muon->muonType() == xAOD::Muon::MuonStandAlone) {
547 return muon;
548 }
549 }
550
551 return nullptr;
552}
553
554// check if truth particle originated from decay of particle in the pdgIdList
555int InDetSecVtxTruthMatchTool::checkProduction( const xAOD::TruthParticle & truthPart, std::vector<const xAOD::TruthVertex*> truthVerticesToMatch ) const {
556
557 if (truthPart.nParents() == 0){
558 ATH_MSG_DEBUG("Particle has no parents (end of loop)");
560 }
561 else{
562 const xAOD::TruthParticle * parent = truthPart.parent(0);
563 if(not parent) {
564 ATH_MSG_DEBUG("Particle parent is null");
566 }
567 ATH_MSG_DEBUG("Parent ID: " << parent->pdgId());
568
569 const xAOD::TruthVertex* parentVertex = parent->decayVtx();
570 if(std::find(truthVerticesToMatch.begin(), truthVerticesToMatch.end(), parentVertex) != truthVerticesToMatch.end()) {
571 ATH_MSG_DEBUG("Found LLP decay.");
572 return HepMC::uniqueID(parentVertex);
573 }
574 // recurse on parent
575 return checkProduction(*parent, truthVerticesToMatch);
576 }
578}
579
580// secvtx origin has extra categories compared to original track origin, so need to map them to our enum
581int mapTrkOriginToSecVtxOrigin(int trkOriginBits) {
582 int myBits = 0;
583 if (trkOriginBits & (1 << InDet::TrkOrigin::BHadronDecay))
585 if (trkOriginBits & (1 << InDet::TrkOrigin::DHadronDecay))
587 if (trkOriginBits & (1 << InDet::TrkOrigin::TauDecay))
589 if (trkOriginBits & (1 << InDet::TrkOrigin::GammaConversion))
591 if (trkOriginBits & (1 << InDet::TrkOrigin::StrangeMesonDecay))
593 if (trkOriginBits & (1 << InDet::TrkOrigin::KshortDecay))
595 if (trkOriginBits & (1 << InDet::TrkOrigin::StrangeBaryonDecay))
597 if (trkOriginBits & (1 << InDet::TrkOrigin::LambdaDecay))
599 if (trkOriginBits & (1 << InDet::TrkOrigin::OtherDecay))
601 if (trkOriginBits & (1 << InDet::TrkOrigin::HadronicInteraction))
603 if (trkOriginBits & (1 << InDet::TrkOrigin::OtherSecondary))
605 if (trkOriginBits & (1 << InDet::TrkOrigin::Fragmentation))
607 if (trkOriginBits & (1 << InDet::TrkOrigin::OtherOrigin))
609 return myBits;
610}
611
613 if (m_trackTruthOriginTool.isSet()) {
614 int trkOriginBits = m_trackTruthOriginTool->getTruthOrigin(&truthPart);
615 return mapTrkOriginToSecVtxOrigin(trkOriginBits);
616 }
617 ATH_MSG_WARNING("TrackTruthOriginTool not set, returning 0 for origin");
618 return 0;
619}
620
622 std::vector<const xAOD::TruthParticle*>& set, int counter) const {
623
624 counter++;
625
626 for (size_t itrk = 0; itrk < signalTruthVertex.nOutgoingParticles(); itrk++) {
627 const auto* particle = signalTruthVertex.outgoingParticle(itrk);
628 if (!particle) continue;
629
630 auto isInsideID = [](const TVector3& v) { return (v.Perp() < 300. && std::abs(v.z()) < 1500.); };
631 auto isOutsideID = [](const TVector3& v) { return (v.Perp() > 563. || std::abs(v.z()) > 2720.); };
632 auto isWithinMuSAWindow = [](const TVector3& v) { return (v.Perp() < 8000. && std::abs(v.z()) < 10000.); };
633
634 // Recursively add descendents
635 if (particle->hasDecayVtx()) {
636
637 TVector3 decayPos(particle->decayVtx()->x(), particle->decayVtx()->y(), particle->decayVtx()->z());
638 TVector3 prodPos(particle->prodVtx()->x(), particle->prodVtx()->y(), particle->prodVtx()->z());
639
640 const auto distance = (decayPos - prodPos).Mag();
641
642 if (counter > 100) {
643 ATH_MSG_WARNING("Vetoing particle that may be added recursively infinitely (potential loop in generator record");
644 break;
645 }
646
647 // consider track reconstructible if it travels at least 10mm
648 if (distance < 10.0) {
649 countReconstructibleDescendentParticles(*particle->decayVtx(), set, counter);
650 } else if (m_doMuSA) {
651 // MuSA: consider particles within the MS window to study reconstruction turn-on
652 if (isWithinMuSAWindow(decayPos) && (particle->isCharged() || particle->isMuon())) {
653 set.push_back(particle);
654 }
655 } else {
656 // Regular tracking: particle originates inside ID and ends outside ID
657 if (isInsideID(prodPos) && isOutsideID(decayPos) && particle->isCharged()) {
658 set.push_back(particle);
659 } else if (particle->isElectron() || particle->isMuon()) {
660 set.push_back(particle);
661 }
662 }
663 } else {
664 if (!(particle->isCharged())) continue;
665 // For particles without decay vertex, include them if they're charged
666 if (m_doMuSA) {
667 const xAOD::TruthVertex* prodVtx = particle->prodVtx();
668 if (prodVtx) {
669 TVector3 prodPos(prodVtx->x(), prodVtx->y(), prodVtx->z());
670 if (isWithinMuSAWindow(prodPos)) {
671 set.push_back(particle);
672 }
673 }
674 } else {
675 set.push_back(particle);
676 }
677 }
678 }
679}
680
681bool InDetSecVtxTruthMatchTool::isFrom(const xAOD::TruthParticle& truth, int flav) const {
682 if (m_trackTruthOriginTool.isSet()) {
683 return m_trackTruthOriginTool->isFrom(&truth, flav);
684 }
685 ATH_MSG_WARNING("TrackTruthOriginTool not set, returning false for isFrom");
686 return false;
687}
688
689
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
bool isValid(const T &p)
Av: we implement here an ATLAS-sepcific convention: all particles which are 99xxxxx are fine.
Definition AtlasPID.h:878
ATLAS-specific HepMC functions.
int mapTrkOriginToSecVtxOrigin(int trkOriginBits)
xAOD::MuonContainer * muonContainer
ServiceHandle< StoreGateSvc > & evtStore()
ToolHandle< InDet::IInDetTrackTruthOriginTool > m_trackTruthOriginTool
Gaudi::Property< std::string > m_selectedTrackFlag
void countReconstructibleDescendentParticles(const xAOD::TruthVertex &signalTruthVertex, std::vector< const xAOD::TruthParticle * > &set, int counter) const
virtual StatusCode initialize() override final
Dummy implementation of the initialisation function.
virtual StatusCode matchVertices(std::vector< const xAOD::Vertex * > recoVerticesToMatch, std::vector< const xAOD::TruthVertex * > truthVerticesToMatch, const xAOD::TrackParticleContainer *trackParticles) override
Gaudi::Property< bool > m_doSMOrigin
InDetSecVtxTruthMatchTool(const std::string &name)
Gaudi::Property< std::string > m_muonFallbackContainerName
int checkProduction(const xAOD::TruthParticle &truthPart, std::vector< const xAOD::TruthVertex * > truthVerticesToMatch) const
int checkSMProduction(const xAOD::TruthParticle &truthPart) const
const xAOD::Muon * findStandAloneMuon(const xAOD::TrackParticle &mstp, const xAOD::MuonContainer *muonContainer) const
std::vector< int > checkParticle(const xAOD::TruthParticle &part, const xAOD::TrackParticleContainer *tkCont, const xAOD::MuonContainer *muonCont) const
Gaudi::Property< std::string > m_muonContainerName
Gaudi::Property< float > m_trkPtCut
Gaudi::Property< float > m_vxMatchWeight
Gaudi::Property< float > m_trkMatchProb
bool isFrom(const xAOD::TruthParticle &truth, int flav) const
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:573
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
SG::Decorator< T, ALLOC > Decorator
class to provide type-safe access to aux data.
Definition AuxElement.h:135
SG::ConstAccessor< T, ALLOC > ConstAccessor
Helper class to provide type-safe access to aux data.
Definition AuxElement.h:131
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
STL class.
bool parameter(float &value, const ParamDef parameter) const
Get a parameter for this Muon - momentumBalanceSignificance for example.
virtual double pt() const override final
The transverse momentum ( ) of the particle.
const TruthParticle_v1 * parent(size_t i) const
Retrieve the i-th mother (TruthParticle) of this TruthParticle.
virtual double pt() const override final
The transverse momentum ( ) of the particle.
size_t nParents() const
Number of parents of this particle.
const TruthParticle_v1 * outgoingParticle(size_t index) const
Get one of the outgoing particles.
float z() const
Vertex longitudinal distance along the beam line form the origin.
float y() const
Vertex y displacement.
size_t nOutgoingParticles() const
Get the number of outgoing particles.
float x() const
Vertex x displacement.
std::vector< ElementLink< xAOD::TrackParticleContainer > > TrackParticleLinks_t
Type for the associated track particles.
Definition Vertex_v1.h:128
int uniqueID(const T &p)
bool is_same_particle(const T1 &p1, const T2 &p2)
Method to establish if two particles in the GenEvent actually represent the same particle.
constexpr int INVALID_VERTEX_ID
std::tuple< ElementLink< xAOD::TruthVertexContainer >, float, float > VertexTruthMatchInfo
TruthVertex_v1 TruthVertex
Typedef to implementation.
Definition TruthVertex.h:15
TrackParticle_v1 TrackParticle
Reference the current persistent version:
TruthVertexContainer_v1 TruthVertexContainer
Declare the latest version of the truth vertex container.
VertexContainer_v1 VertexContainer
Definition of the current "Vertex container version".
Vertex_v1 Vertex
Define the latest version of the vertex class.
TruthParticle_v1 TruthParticle
Typedef to implementation.
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".
Muon_v1 Muon
Reference the current persistent version:
MuonContainer_v1 MuonContainer
Definition of the current "Muon container version".