Pandora
Pandora source code navigator
Loading...
Searching...
No Matches
LArHierarchyHelper.cc
Go to the documentation of this file.
1
9#include "Pandora/PdgTable.h"
10#include "Pandora/StatusCodes.h"
11
14
15#include <numeric>
16
17namespace lar_content
18{
19
20using namespace pandora;
21
23 m_foldToLeadingShowers{false},
24 m_foldToTier{false},
25 m_foldDynamic{false},
26 m_cosAngleTolerance{0.9962f},
27 m_tier{1}
28{
29}
30
31//------------------------------------------------------------------------------------------------------------------------------------------
32
33LArHierarchyHelper::FoldingParameters::FoldingParameters(const bool foldDynamic, const float cosAngleTolerance) :
34 m_foldToLeadingShowers{false},
35 m_foldToTier{false},
36 m_foldDynamic{foldDynamic},
37 m_cosAngleTolerance{cosAngleTolerance},
38 m_tier{1}
39{
40}
41
42//------------------------------------------------------------------------------------------------------------------------------------------
43
45 m_foldToLeadingShowers{false},
46 m_foldToTier{true},
47 m_foldDynamic{false},
48 m_cosAngleTolerance{0.9962f},
49 m_tier{foldingTier}
50{
51 if (m_tier < 1)
52 {
53 std::cout << "LArHierarchyHelper: Error - attempting to fold to non-positive tier" << std::endl;
54 throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
55 }
56}
57
58//------------------------------------------------------------------------------------------------------------------------------------------
59//------------------------------------------------------------------------------------------------------------------------------------------
60
61LArHierarchyHelper::QualityCuts::QualityCuts() : m_minPurity{0.8f}, m_minCompleteness{0.65f}
62{
63}
64
65//------------------------------------------------------------------------------------------------------------------------------------------
66
67LArHierarchyHelper::QualityCuts::QualityCuts(const float minPurity, const float minCompleteness) :
68 m_minPurity{minPurity},
69 m_minCompleteness{minCompleteness}
70{
71}
72
73//------------------------------------------------------------------------------------------------------------------------------------------
74//------------------------------------------------------------------------------------------------------------------------------------------
75
79
80//------------------------------------------------------------------------------------------------------------------------------------------
81
82LArHierarchyHelper::MCHierarchy::MCHierarchy(const ReconstructabilityCriteria &recoCriteria) : m_recoCriteria(recoCriteria), m_nextNodeId{1}
83{
84}
85
86//------------------------------------------------------------------------------------------------------------------------------------------
87
89{
90 for (const auto &[pRoot, nodeVector] : m_interactions)
91 {
92 (void)pRoot;
93 for (const Node *pNode : nodeVector)
94 delete pNode;
95 }
96 m_interactions.clear();
97}
98
99//------------------------------------------------------------------------------------------------------------------------------------------
100
101void LArHierarchyHelper::MCHierarchy::FillHierarchy(const MCParticleList &mcParticleList, const CaloHitList &caloHitList, const FoldingParameters &foldParameters)
102{
103 const auto predicate = [](const MCParticle *pMCParticle) { return std::abs(pMCParticle->GetParticleId()) == NEUTRON; };
104 m_mcToHitsMap.clear();
105 for (const CaloHit *pCaloHit : caloHitList)
106 {
107 try
108 {
109 const MCParticle *pMCParticle{MCParticleHelper::GetMainMCParticle(pCaloHit)};
110 m_mcToHitsMap[pMCParticle].emplace_back(pCaloHit);
111 }
112 catch (const StatusCodeException &)
113 {
114 }
115 }
116
117 MCParticleList rootNodes;
118 for (const MCParticle *pMCParticle : mcParticleList)
119 {
120 const MCParticleList &parentList{pMCParticle->GetParentList()};
121 if (parentList.empty())
122 {
123 rootNodes.emplace_back(pMCParticle);
124 }
125 }
126
127 for (const MCParticle *pRoot : rootNodes)
128 {
129 MCParticleSet primarySet;
130 LArHierarchyHelper::GetMCPrimaries(pRoot, primarySet);
131 MCParticleList primaries(primarySet.begin(), primarySet.end());
133 if (m_recoCriteria.m_removeNeutrons)
134 primaries.erase(std::remove_if(primaries.begin(), primaries.end(), predicate), primaries.end());
135 if (foldParameters.m_foldToTier && foldParameters.m_tier == 1)
136 {
137 for (const MCParticle *pPrimary : primaries)
138 {
139 MCParticleList allParticles{pPrimary};
140 if (!m_recoCriteria.m_removeNeutrons)
141 {
143 }
144 else
145 {
146 // Collect track-like and shower-like particles together, but throw out neutrons and descendents
147 MCParticleList dummy;
148 LArMCParticleHelper::GetAllDescendentMCParticles(pPrimary, allParticles, allParticles, dummy);
149 }
150 CaloHitList allHits;
151 for (const MCParticle *pMCParticle : allParticles)
152 {
153 // Not all MC particles will have hits
154 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
155 {
156 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
157 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
158 }
159 }
160 m_interactions[pRoot].emplace_back(new Node(*this, allParticles, allHits));
161 }
162 }
163 else if (foldParameters.m_foldToLeadingShowers)
164 {
165 for (const MCParticle *pPrimary : primaries)
166 {
167 MCParticleList allParticles{pPrimary};
168 int pdg{std::abs(pPrimary->GetParticleId())};
169 const bool isShower{pdg == E_MINUS || pdg == PHOTON};
170 const bool isNeutron{pdg == NEUTRON};
171 if (isShower || (isNeutron && !m_recoCriteria.m_removeNeutrons))
173 CaloHitList allHits;
174 for (const MCParticle *pMCParticle : allParticles)
175 {
176 // ATTN - Not all MC particles will have hits
177 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
178 {
179 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
180 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
181 }
182 }
183 Node *pNode{new Node(*this, allParticles, allHits)};
184 m_interactions[pRoot].emplace_back(pNode);
185 if (!(isShower || isNeutron))
186 {
187 // Find the children of this particle and recursively add them to the hierarchy
188 const MCParticleList &children{pPrimary->GetDaughterList()};
189 for (const MCParticle *pChild : children)
190 pNode->FillHierarchy(pChild, foldParameters);
191 }
192 }
193 }
194 else if (foldParameters.m_foldDynamic)
195 {
196 for (const MCParticle *pPrimary : primaries)
197 {
198 MCParticleList leadingParticles, childParticles;
199 this->InterpretHierarchy(pPrimary, leadingParticles, childParticles, foldParameters.m_cosAngleTolerance);
200 CaloHitList allHits;
201 for (const MCParticle *pMCParticle : leadingParticles)
202 {
203 // ATTN - Not all MC particles will have hits
204 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
205 {
206 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
207 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
208 }
209 }
210
211 Node *pNode{new Node(*this, leadingParticles, allHits)};
212 m_interactions[pRoot].emplace_back(pNode);
213 for (const MCParticle *pChild : childParticles)
214 pNode->FillHierarchy(pChild, foldParameters);
215 }
216 }
217 else
218 {
219 // Unfolded and folded to tier > 1 have the same behaviour for primaries
220 for (const MCParticle *pPrimary : primaries)
221 {
222 MCParticleList allParticles{pPrimary};
223 CaloHitList allHits;
224 for (const MCParticle *pMCParticle : allParticles)
225 {
226 // ATTN - Not all MC particles will have hits
227 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
228 {
229 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
230 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
231 }
232 }
233 Node *pNode{new Node(*this, allParticles, allHits)};
234 m_interactions[pRoot].emplace_back(pNode);
235 // Find the children of this particle and recursively add them to the hierarchy
236 const MCParticleList &children{pPrimary->GetDaughterList()};
237 for (const MCParticle *pChild : children)
238 pNode->FillHierarchy(pChild, foldParameters);
239 }
240 }
241
242 Node *pLeadingLepton{nullptr};
243 float leadingLeptonEnergy{-std::numeric_limits<float>::max()};
244 for (const Node *pNode : m_interactions[pRoot])
245 {
246 const MCParticle *pMC{pNode->GetLeadingMCParticle()};
247 if (pMC)
248 {
249 const int pdg{std::abs(pMC->GetParticleId())};
250 if ((pdg == MU_MINUS || pdg == E_MINUS || pdg == TAU_MINUS) && pMC->GetEnergy() > leadingLeptonEnergy)
251 {
252 pLeadingLepton = const_cast<Node *>(pNode);
253 leadingLeptonEnergy = pMC->GetEnergy();
254 }
255 }
256 }
257 if (pLeadingLepton)
258 pLeadingLepton->SetLeadingLepton();
259 }
260}
261
262//------------------------------------------------------------------------------------------------------------------------------------------
263
265{
266 if (m_interactions.find(pRoot) == m_interactions.end())
267 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
268
269 return m_interactions.at(pRoot);
270}
271
272//------------------------------------------------------------------------------------------------------------------------------------------
273
275{
276 for (auto iter = m_interactions.begin(); iter != m_interactions.end(); ++iter)
277 rootMCParticles.emplace_back(iter->first);
278}
279
280//------------------------------------------------------------------------------------------------------------------------------------------
281
283 const MCParticle *const pRoot, MCParticleList &leadingParticles, MCParticleList &childParticles, const float cosAngleTolerance) const
284{
285 leadingParticles.emplace_back(pRoot);
286 MCParticleList foldCandidates, childCandidates;
287 const MCParticleList &children{pRoot->GetDaughterList()};
288 for (const MCParticle *pMCParticle : children)
289 {
290 const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
291 if (!pLArMCParticle)
292 continue;
294 {
295 // Elastic and inelastic scattering can either lead to folding, distinct nodes or disposable hits, all other processes
296 // are either distinct nodes, or disposable
297 if (pMCParticle->GetParticleId() == pRoot->GetParticleId())
298 foldCandidates.emplace_back(pMCParticle);
299 else
300 childCandidates.emplace_back(pMCParticle);
301 }
302 else if (!m_recoCriteria.m_removeNeutrons || (m_recoCriteria.m_removeNeutrons && pLArMCParticle->GetProcess() != MC_PROC_N_CAPTURE))
303 {
304 // Non-scattering process particles become leading candidates unless it's neutron capture and we're removing neutrons
305 childCandidates.emplace_back(pMCParticle);
306 }
307 }
308 const MCParticle *pBestFoldCandidate{nullptr};
309 float bestDp{std::numeric_limits<float>::max()};
310 for (const MCParticle *pMCParticle : foldCandidates)
311 {
312 if (foldCandidates.size() == 1)
313 {
314 // No alternative options, so this is either the best folding option by default, or a sufficiently large scatter to
315 // treat as a new particle for reconstruction purposes
316 if (LArMCParticleHelper::AreTopologicallyContinuous(pRoot, pMCParticle, cosAngleTolerance))
317 pBestFoldCandidate = pMCParticle;
318 else
319 childCandidates.emplace_back(pMCParticle);
320 }
321 else
322 {
323 // Assess which, if any, of the children might be a continuation of the trajectory, otherwise move to child candidates
324 if (LArMCParticleHelper::AreTopologicallyContinuous(pRoot, pMCParticle, cosAngleTolerance))
325 {
326 const float dp{pRoot->GetMomentum().GetMagnitude() - pMCParticle->GetMomentum().GetMagnitude()};
327 if (dp < bestDp)
328 {
329 pBestFoldCandidate = pMCParticle;
330 bestDp = dp;
331 }
332 }
333 else
334 {
335 childCandidates.emplace_back(pMCParticle);
336 }
337 }
338 }
339 if (pBestFoldCandidate)
340 {
341 leadingParticles.emplace_back(pBestFoldCandidate);
342 // Having found a particle to fold back at this level, continue to explore its downstream hierarchy for further folding
343 // opportunities and make their respective children leading particles for the folded node we are creating
344 this->CollectContinuations(pBestFoldCandidate, leadingParticles, childCandidates, cosAngleTolerance);
345 }
346 for (const MCParticle *pMCParticle : childCandidates)
347 {
348 // Consider if the child particle will produce enough downstream hits to warrant inclusion
349 if (this->IsReconstructable(pMCParticle))
350 childParticles.emplace_back(pMCParticle);
351 else
352 {
353 MCParticleList localHierarchy{pMCParticle};
354 CaloHitList localHits;
355 LArMCParticleHelper::GetAllDescendentMCParticles(pMCParticle, localHierarchy);
356 for (const MCParticle *pLocalMCParticle : localHierarchy)
357 {
358 if (m_mcToHitsMap.find(pLocalMCParticle) != m_mcToHitsMap.end())
359 {
360 const CaloHitList &caloHits(m_mcToHitsMap.at(pLocalMCParticle));
361 localHits.insert(localHits.begin(), caloHits.begin(), caloHits.end());
362 }
363 }
364 if (this->IsReconstructable(localHits))
365 childParticles.emplace_back(pMCParticle);
366 }
367 }
368}
369
370//------------------------------------------------------------------------------------------------------------------------------------------
371
373 const MCParticle *pRoot, MCParticleList &continuingParticles, MCParticleList &childParticles, const float cosAngleTolerance) const
374{
375 const MCParticleList &children{pRoot->GetDaughterList()};
376 MCParticleList foldCandidates;
377 for (const MCParticle *pMCParticle : children)
378 {
379 const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
380 if (!pLArMCParticle)
381 continue;
382 // Only elastic and inelastic scattering can lead to folding
384 {
385 if (pMCParticle->GetParticleId() == pRoot->GetParticleId())
386 foldCandidates.emplace_back(pMCParticle);
387 }
388 else if (!m_recoCriteria.m_removeNeutrons || (m_recoCriteria.m_removeNeutrons && pLArMCParticle->GetProcess() != MC_PROC_N_CAPTURE))
389 {
390 // Non-scattering process particles become leading candidates unless it's neutron capture and we're removing neutrons
391 childParticles.emplace_back(pMCParticle);
392 }
393 }
394 const MCParticle *pBestFoldCandidate{nullptr};
395 float bestDp{std::numeric_limits<float>::max()};
396 for (const MCParticle *pMCParticle : foldCandidates)
397 {
398 if (foldCandidates.size() == 1)
399 {
400 // No alternative options, so this is either the best folding option by default, or a sufficiently large scatter to
401 // treat as a new particle for reconstruction purposes
402 if (LArMCParticleHelper::AreTopologicallyContinuous(pRoot, pMCParticle, cosAngleTolerance))
403 pBestFoldCandidate = pMCParticle;
404 }
405 else
406 {
407 // Assess which, if any, of the children might be a continuation of the trajectory, otherwise move to child candidates
408 if (LArMCParticleHelper::AreTopologicallyContinuous(pRoot, pMCParticle, cosAngleTolerance))
409 {
410 const float dp{pRoot->GetMomentum().GetMagnitude() - pMCParticle->GetMomentum().GetMagnitude()};
411 if (dp < bestDp)
412 {
413 pBestFoldCandidate = pMCParticle;
414 bestDp = dp;
415 }
416 }
417 }
418 }
419 if (pBestFoldCandidate)
420 {
421 continuingParticles.emplace_back(pBestFoldCandidate);
422 const MCParticleList &newLeadingParticles{pBestFoldCandidate->GetDaughterList()};
423 // We need to add the children as child particles to ensure these sub-hierarchies are explored...
424 childParticles.insert(childParticles.begin(), newLeadingParticles.begin(), newLeadingParticles.end());
425 // but this current best fold candidate may have been added to the child particles by previously, so remove it
426 const auto iter{std::find(childParticles.begin(), childParticles.end(), pBestFoldCandidate)};
427 if (iter != childParticles.end())
428 childParticles.erase(iter);
429 // Having found a particle to fold back at this level, continue to explore its downstream hierarchy for further folding
430 // opportunities and make their respective children child particles for the folded node we are creating
431 LArHierarchyHelper::MCHierarchy::CollectContinuations(pBestFoldCandidate, continuingParticles, childParticles, cosAngleTolerance);
432 }
433}
434
435//------------------------------------------------------------------------------------------------------------------------------------------
436
438{
439 NodeList queue;
440 for (const Node *pNode : m_interactions.at(pRoot))
441 {
442 nodeVector.emplace_back(pNode);
443 queue.emplace_back(pNode);
444 }
445 while (!queue.empty())
446 {
447 const NodeVector &children{queue.front()->GetChildren()};
448 queue.pop_front();
449 for (const Node *pChild : children)
450 {
451 nodeVector.emplace_back(pChild);
452 queue.emplace_back(pChild);
453 }
454 }
455}
456
457//------------------------------------------------------------------------------------------------------------------------------------------
458
460{
461 m_nodeToIdMap.insert(std::make_pair(pNode, m_nextNodeId));
462 ++m_nextNodeId;
463}
464
465//------------------------------------------------------------------------------------------------------------------------------------------
466
468{
469 std::string str;
470 for (const auto &[pRoot, nodeVector] : m_interactions)
471 {
472 const LArMCParticle *const pLArRoot{dynamic_cast<const LArMCParticle *const>(pRoot)};
473 if (pLArRoot)
474 str += "=== MC Interaction : PDG " + std::to_string(pLArRoot->GetParticleId()) +
475 " Energy: " + std::to_string(pLArRoot->GetEnergy()) + " Nuance: " + std::to_string(pLArRoot->GetNuanceCode()) + "\n";
476 else
477 str += "=== MC Interaction : PDG " + std::to_string(pRoot->GetParticleId()) + " Energy: " + std::to_string(pRoot->GetEnergy()) + "\n";
478 for (const Node *pNode : nodeVector)
479 str += " " + pNode->ToString("") + "\n";
480 }
481
482 return str;
483}
484
485//------------------------------------------------------------------------------------------------------------------------------------------
486
488{
489 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
490 {
491 unsigned int nHitsU{0}, nHitsV{0}, nHitsW{0};
492 for (const CaloHit *pCaloHit : m_mcToHitsMap.at(pMCParticle))
493 {
494 const HitType view{pCaloHit->GetHitType()};
495 if (view == TPC_VIEW_U)
496 ++nHitsU;
497 else if (view == TPC_VIEW_V)
498 ++nHitsV;
499 else if (view == TPC_VIEW_W)
500 ++nHitsW;
501 }
502 const unsigned int nHits{nHitsU + nHitsV + nHitsW};
503 unsigned int nGoodViews{0};
504 nGoodViews += nHitsU >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
505 nGoodViews += nHitsV >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
506 nGoodViews += nHitsW >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
507
508 return nHits >= m_recoCriteria.m_minHits && nGoodViews >= m_recoCriteria.m_minGoodViews;
509 }
510
511 return false;
512}
513
514//------------------------------------------------------------------------------------------------------------------------------------------
515
517{
518 unsigned int nHitsU{0}, nHitsV{0}, nHitsW{0};
519 for (const CaloHit *pCaloHit : caloHits)
520 {
521 const HitType view{pCaloHit->GetHitType()};
522 if (view == TPC_VIEW_U)
523 ++nHitsU;
524 else if (view == TPC_VIEW_V)
525 ++nHitsV;
526 else if (view == TPC_VIEW_W)
527 ++nHitsW;
528 }
529 const unsigned int nHits{nHitsU + nHitsV + nHitsW};
530 unsigned int nGoodViews{0};
531 nGoodViews += nHitsU >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
532 nGoodViews += nHitsV >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
533 nGoodViews += nHitsW >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
534
535 return nHits >= m_recoCriteria.m_minHits && nGoodViews >= m_recoCriteria.m_minGoodViews;
536}
537
538//------------------------------------------------------------------------------------------------------------------------------------------
539//------------------------------------------------------------------------------------------------------------------------------------------
540
541LArHierarchyHelper::MCHierarchy::Node::Node(MCHierarchy &hierarchy, const MCParticle *pMCParticle, const int tier) :
542 m_hierarchy(hierarchy),
543 m_mainParticle(pMCParticle),
544 m_tier{tier},
545 m_pdg{0},
546 m_isLeadingLepton{false}
547{
548 if (pMCParticle)
549 {
550 m_pdg = pMCParticle->GetParticleId();
551 m_mcParticles.emplace_back(pMCParticle);
552 }
554}
555
556//------------------------------------------------------------------------------------------------------------------------------------------
557
558LArHierarchyHelper::MCHierarchy::Node::Node(MCHierarchy &hierarchy, const MCParticleList &mcParticleList, const CaloHitList &caloHitList, const int tier) :
559 m_hierarchy(hierarchy),
560 m_mcParticles(mcParticleList),
561 m_caloHits(caloHitList),
562 m_mainParticle(nullptr),
563 m_tier{tier},
564 m_pdg{0},
565 m_isLeadingLepton{false}
566{
567 if (!mcParticleList.empty())
568 {
569 m_mainParticle = mcParticleList.front();
571 }
573 m_caloHits.sort();
575}
576
577//------------------------------------------------------------------------------------------------------------------------------------------
578
580{
581 m_mcParticles.clear();
582 m_caloHits.clear();
583 for (const Node *node : m_children)
584 delete node;
585 m_children.clear();
586}
587
588//------------------------------------------------------------------------------------------------------------------------------------------
589
591{
592 if (foldParameters.m_foldDynamic)
593 {
594 MCParticleList leadingParticles, childParticles;
595 m_hierarchy.InterpretHierarchy(pRoot, leadingParticles, childParticles, foldParameters.m_cosAngleTolerance);
596 CaloHitList allHits;
597 for (const MCParticle *pMCParticle : leadingParticles)
598 {
599 // ATTN - Not all MC particles will have hits
600 if (m_hierarchy.m_mcToHitsMap.find(pMCParticle) != m_hierarchy.m_mcToHitsMap.end())
601 {
602 const CaloHitList &caloHits(m_hierarchy.m_mcToHitsMap.at(pMCParticle));
603 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
604 }
605 }
606
607 Node *pNode{new Node(m_hierarchy, leadingParticles, allHits, this->m_tier + 1)};
608 m_children.emplace_back(pNode);
609 for (const MCParticle *pChild : childParticles)
610 pNode->FillHierarchy(pChild, foldParameters);
611 }
612 else
613 {
614 MCParticleList allParticles{pRoot};
615 const int pdg{std::abs(pRoot->GetParticleId())};
616 const bool isShower{pdg == E_MINUS || pdg == PHOTON};
617 const bool isNeutron{pdg == NEUTRON};
618
619 if (foldParameters.m_foldToTier && LArMCParticleHelper::GetHierarchyTier(pRoot) >= foldParameters.m_tier)
621 else if (foldParameters.m_foldToLeadingShowers && (isShower || (isNeutron && !m_hierarchy.m_recoCriteria.m_removeNeutrons)))
623 else if (m_hierarchy.m_recoCriteria.m_removeNeutrons && isNeutron)
624 return;
625
626 CaloHitList allHits;
627 for (const MCParticle *pMCParticle : allParticles)
628 {
629 // ATTN - Not all MC particles will have hits
630 if (m_hierarchy.m_mcToHitsMap.find(pMCParticle) != m_hierarchy.m_mcToHitsMap.end())
631 {
632 const CaloHitList &caloHits(m_hierarchy.m_mcToHitsMap.at(pMCParticle));
633 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
634 }
635 }
636
637 if (!allParticles.empty())
638 {
639 const bool hasChildren{(foldParameters.m_foldToTier && LArMCParticleHelper::GetHierarchyTier(pRoot) < foldParameters.m_tier) ||
640 (!foldParameters.m_foldToTier && !foldParameters.m_foldToLeadingShowers) ||
641 (foldParameters.m_foldToLeadingShowers && !(isShower || isNeutron))};
642 // Only add the node if it either has children, or is a leaf node with hits
643 if (hasChildren || (!hasChildren && !allHits.empty()))
644 {
645 Node *pNode{new Node(m_hierarchy, allParticles, allHits, this->m_tier + 1)};
646 m_children.emplace_back(pNode);
647 if (hasChildren)
648 {
649 // Find the children of this particle and recursively add them to the hierarchy
650 const MCParticleList &children{pRoot->GetDaughterList()};
651 for (const MCParticle *pChild : children)
652 pNode->FillHierarchy(pChild, foldParameters);
653 }
654 }
655 }
656 }
657}
658
659//------------------------------------------------------------------------------------------------------------------------------------------
660
662{
663 MCParticleList allParticles{pRoot};
664 if (!m_hierarchy.m_recoCriteria.m_removeNeutrons)
665 {
667 }
668 else
669 {
670 MCParticleList neutrons;
671 LArMCParticleHelper::GetAllDescendentMCParticles(pRoot, allParticles, allParticles, neutrons);
672 }
673 CaloHitList allHits;
674 for (const MCParticle *pMCParticle : allParticles)
675 {
676 // ATTN - Not all MC particles will have hits
677 if (m_hierarchy.m_mcToHitsMap.find(pMCParticle) != m_hierarchy.m_mcToHitsMap.end())
678 {
679 const CaloHitList &caloHits(m_hierarchy.m_mcToHitsMap.at(pMCParticle));
680 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
681 }
682 }
683 if (!allParticles.empty())
684 {
685 Node *pNode{new Node(m_hierarchy, allParticles, allHits, this->m_tier + 1)};
686 m_children.emplace_back(pNode);
687 }
688}
689
690//------------------------------------------------------------------------------------------------------------------------------------------
691
693{
694 return m_hierarchy.m_nodeToIdMap.at(this);
695}
696
697//------------------------------------------------------------------------------------------------------------------------------------------
698
700{
701 const bool enoughHits{m_caloHits.size() >= m_hierarchy.m_recoCriteria.m_minHits};
702 if (!enoughHits)
703 return false;
704 bool enoughGoodViews{false};
705 unsigned int nHitsU{0}, nHitsV{0}, nHitsW{0};
706 for (const CaloHit *pCaloHit : m_caloHits)
707 {
708 switch (pCaloHit->GetHitType())
709 {
710 case TPC_VIEW_U:
711 ++nHitsU;
712 break;
713 case TPC_VIEW_V:
714 ++nHitsV;
715 break;
716 case TPC_VIEW_W:
717 ++nHitsW;
718 break;
719 default:
720 break;
721 }
722 unsigned int nGoodViews{0};
723 if (nHitsU >= m_hierarchy.m_recoCriteria.m_minHitsForGoodView)
724 ++nGoodViews;
725 if (nHitsV >= m_hierarchy.m_recoCriteria.m_minHitsForGoodView)
726 ++nGoodViews;
727 if (nHitsW >= m_hierarchy.m_recoCriteria.m_minHitsForGoodView)
728 ++nGoodViews;
729 if (nGoodViews >= m_hierarchy.m_recoCriteria.m_minGoodViews)
730 {
731 enoughGoodViews = true;
732 break;
733 }
734 }
735
736 return enoughGoodViews;
737}
738
739//------------------------------------------------------------------------------------------------------------------------------------------
740
742{
743 if (m_mainParticle)
744 return LArMCParticleHelper::IsBeamParticle(m_mainParticle);
745 else
746 return false;
747}
748
749//------------------------------------------------------------------------------------------------------------------------------------------
750
752{
753 if (m_mainParticle)
754 return LArMCParticleHelper::IsCosmicRay(m_mainParticle);
755 else
756 return false;
757}
758
759//------------------------------------------------------------------------------------------------------------------------------------------
760
761const std::string LArHierarchyHelper::MCHierarchy::Node::ToString(const std::string &prefix) const
762{
763 std::string str(prefix + "PDG: " + std::to_string(m_pdg) + " Energy: " + std::to_string(m_mainParticle ? m_mainParticle->GetEnergy() : 0) +
764 " Hits: " + std::to_string(m_caloHits.size()) + "\n");
765 for (const Node *pChild : m_children)
766 str += pChild->ToString(prefix + " ");
767
768 return str;
769}
770
771//------------------------------------------------------------------------------------------------------------------------------------------
772//------------------------------------------------------------------------------------------------------------------------------------------
773
775 m_minHits{30},
776 m_minHitsForGoodView{10},
777 m_minGoodViews{2},
778 m_removeNeutrons{true}
779{
780}
781
782//------------------------------------------------------------------------------------------------------------------------------------------
783
785 m_minHits{obj.m_minHits},
786 m_minHitsForGoodView{obj.m_minHitsForGoodView},
787 m_minGoodViews{obj.m_minGoodViews},
788 m_removeNeutrons{obj.m_removeNeutrons}
789{
790}
791
792//------------------------------------------------------------------------------------------------------------------------------------------
793
795 const unsigned int minHits, const unsigned int minHitsForGoodView, const unsigned int minGoodViews, const bool removeNeutrons) :
796 m_minHits{minHits},
797 m_minHitsForGoodView{minHitsForGoodView},
798 m_minGoodViews{minGoodViews},
799 m_removeNeutrons{removeNeutrons}
800{
801}
802
803//------------------------------------------------------------------------------------------------------------------------------------------
804//------------------------------------------------------------------------------------------------------------------------------------------
805
809
810//------------------------------------------------------------------------------------------------------------------------------------------
811
813{
814 for (const auto &[pRoot, nodeVector] : m_interactions)
815 {
816 for (const Node *pNode : nodeVector)
817 delete pNode;
818 }
819 m_interactions.clear();
820}
821
822//------------------------------------------------------------------------------------------------------------------------------------------
823
825{
826 PfoList rootNodes;
827 for (const ParticleFlowObject *pPfo : pfoList)
828 {
829 const PfoList &parentList{pPfo->GetParentPfoList()};
830 if (parentList.empty())
831 {
832 rootNodes.emplace_back(pPfo);
833 }
834 }
835
836 for (const ParticleFlowObject *const pRoot : rootNodes)
837 {
838 PfoSet primarySet;
839 LArHierarchyHelper::GetRecoPrimaries(pRoot, primarySet);
840 PfoList primaries(primarySet.begin(), primarySet.end());
841 primaries.sort(LArPfoHelper::SortByNHits);
842 if (foldParameters.m_foldToTier && foldParameters.m_tier == 1)
843 {
844 for (const ParticleFlowObject *pPrimary : primaries)
845 {
846 PfoList allParticles;
847 // ATTN - pPrimary gets added to the list of downstream PFOs, not just the child PFOs
848 LArPfoHelper::GetAllDownstreamPfos(pPrimary, allParticles);
849 CaloHitList allHits;
850 for (const ParticleFlowObject *pPfo : allParticles)
851 LArPfoHelper::GetAllCaloHits(pPfo, allHits);
852 m_interactions[pRoot].emplace_back(new Node(*this, allParticles, allHits));
853 }
854 }
855 else if (foldParameters.m_foldToLeadingShowers)
856 {
857 for (const ParticleFlowObject *pPrimary : primaries)
858 {
859 PfoList allParticles;
860 int pdg{std::abs(pPrimary->GetParticleId())};
861 const bool isShower{pdg == E_MINUS};
862 // ATTN - pPrimary gets added to the list of downstream PFOs, not just the child PFOs
863 if (isShower)
864 LArPfoHelper::GetAllDownstreamPfos(pPrimary, allParticles);
865 else
866 allParticles.emplace_back(pPrimary);
867
868 CaloHitList allHits;
869 for (const ParticleFlowObject *pPfo : allParticles)
870 LArPfoHelper::GetAllCaloHits(pPfo, allHits);
871 Node *pNode{new Node(*this, allParticles, allHits)};
872 m_interactions[pRoot].emplace_back(pNode);
873 if (!isShower)
874 {
875 // Find the children of this particle and recursively add them to the hierarchy
876 const PfoList &children{pPrimary->GetDaughterPfoList()};
877 for (const ParticleFlowObject *pChild : children)
878 pNode->FillHierarchy(pChild, foldParameters);
879 }
880 }
881 }
882 else
883 {
884 // Dynamic fold, Unfolded and fold to tier > 1 have the same behaviour for primaries
885 for (const ParticleFlowObject *pPrimary : primaries)
886 {
887 PfoList allParticles{pPrimary};
888 CaloHitList allHits;
889 for (const ParticleFlowObject *pPfo : allParticles)
890 LArPfoHelper::GetAllCaloHits(pPfo, allHits);
891 Node *pNode{new Node(*this, allParticles, allHits)};
892 m_interactions[pRoot].emplace_back(pNode);
893 // Find the children of this particle and recursively add them to the hierarchy
894 const PfoList &children{pPrimary->GetDaughterPfoList()};
895 for (const ParticleFlowObject *pChild : children)
896 pNode->FillHierarchy(pChild, foldParameters.m_foldToLeadingShowers);
897 }
898 }
899 }
900}
901
902//------------------------------------------------------------------------------------------------------------------------------------------
903
905{
906 if (m_interactions.find(pRoot) == m_interactions.end())
907 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
908
909 return m_interactions.at(pRoot);
910}
911
912//------------------------------------------------------------------------------------------------------------------------------------------
913
915{
916 for (auto iter = m_interactions.begin(); iter != m_interactions.end(); ++iter)
917 rootPfos.emplace_back(iter->first);
918}
919
920//------------------------------------------------------------------------------------------------------------------------------------------
921
923{
924 NodeList queue;
925 for (const Node *pNode : m_interactions.at(pRoot))
926 {
927 nodeVector.emplace_back(pNode);
928 queue.emplace_back(pNode);
929 }
930 while (!queue.empty())
931 {
932 const NodeVector &children{queue.front()->GetChildren()};
933 queue.pop_front();
934 for (const Node *pChild : children)
935 {
936 nodeVector.emplace_back(pChild);
937 queue.emplace_back(pChild);
938 }
939 }
940}
941
942//------------------------------------------------------------------------------------------------------------------------------------------
943
945{
946 std::string str;
947 for (const auto &[pRoot, nodeVector] : m_interactions)
948 {
949 str += "=== Reco Interaction : PDG " + std::to_string(pRoot->GetParticleId()) + "\n";
950 for (const Node *pNode : nodeVector)
951 str += " " + pNode->ToString("") + "\n";
952 }
953
954 return str;
955}
956
957//------------------------------------------------------------------------------------------------------------------------------------------
958//------------------------------------------------------------------------------------------------------------------------------------------
959
961 m_hierarchy{hierarchy},
962 m_mainPfo{pPfo},
963 m_pdg{0}
964{
965 if (pPfo)
966 {
967 m_pdg = pPfo->GetParticleId();
968 m_pfos.emplace_back(pPfo);
969 }
970}
971
972//------------------------------------------------------------------------------------------------------------------------------------------
973
974LArHierarchyHelper::RecoHierarchy::Node::Node(const RecoHierarchy &hierarchy, const PfoList &pfoList, const CaloHitList &caloHitList) :
975 m_hierarchy(hierarchy),
976 m_mainPfo{nullptr},
977 m_pdg{0}
978{
979 if (!pfoList.empty())
980 {
981 m_mainPfo = pfoList.front();
982 m_pdg = pfoList.front()->GetParticleId();
983 }
984 m_pfos = pfoList;
986 m_caloHits = caloHitList;
987 m_caloHits.sort();
988}
989
990//------------------------------------------------------------------------------------------------------------------------------------------
991
993{
994 m_pfos.clear();
995 m_caloHits.clear();
996 for (const Node *node : m_children)
997 delete node;
998 m_children.clear();
999}
1000
1001//------------------------------------------------------------------------------------------------------------------------------------------
1002
1004{
1005 PfoList allParticles;
1006 int pdg{std::abs(pRoot->GetParticleId())};
1007 const bool isShower{pdg == E_MINUS};
1008 if (foldParameters.m_foldToTier && LArPfoHelper::GetHierarchyTier(pRoot) >= foldParameters.m_tier)
1009 LArPfoHelper::GetAllDownstreamPfos(pRoot, allParticles);
1010 else if (foldParameters.m_foldToLeadingShowers && isShower)
1011 LArPfoHelper::GetAllDownstreamPfos(pRoot, allParticles);
1012 else
1013 allParticles.emplace_back(pRoot);
1014
1015 CaloHitList allHits;
1016 for (const ParticleFlowObject *pPfo : allParticles)
1017 LArPfoHelper::GetAllCaloHits(pPfo, allHits);
1018 const bool hasChildren{(foldParameters.m_foldToTier && LArPfoHelper::GetHierarchyTier(pRoot) < foldParameters.m_tier) ||
1019 (!foldParameters.m_foldToTier && !foldParameters.m_foldToLeadingShowers) ||
1020 (foldParameters.m_foldToLeadingShowers && !isShower)};
1021
1022 if (hasChildren || (!hasChildren && !allHits.empty()))
1023 {
1024 Node *pNode{new Node(m_hierarchy, allParticles, allHits)};
1025 m_children.emplace_back(pNode);
1026
1027 if (hasChildren)
1028 {
1029 const PfoList &children{pRoot->GetDaughterPfoList()};
1030 for (const ParticleFlowObject *pChild : children)
1031 pNode->FillHierarchy(pChild, foldParameters);
1032 }
1033 }
1034}
1035
1036//------------------------------------------------------------------------------------------------------------------------------------------
1037
1039{
1040 PfoList allParticles;
1041 LArPfoHelper::GetAllDownstreamPfos(pRoot, allParticles);
1042 CaloHitList allHits;
1043 for (const ParticleFlowObject *pPfo : allParticles)
1044 LArPfoHelper::GetAllCaloHits(pPfo, allHits);
1045 Node *pNode{new Node(m_hierarchy, allParticles, allHits)};
1046 m_children.emplace_back(pNode);
1047}
1048
1049//------------------------------------------------------------------------------------------------------------------------------------------
1050
1052{
1053 return m_pfos;
1054}
1055
1056//------------------------------------------------------------------------------------------------------------------------------------------
1057
1059{
1060 return m_caloHits;
1061}
1062
1063//------------------------------------------------------------------------------------------------------------------------------------------
1064
1066{
1067 return m_pdg;
1068}
1069
1070//------------------------------------------------------------------------------------------------------------------------------------------
1071
1072const std::string LArHierarchyHelper::RecoHierarchy::Node::ToString(const std::string &prefix) const
1073{
1074 std::string str(prefix + "PDG: " + std::to_string(m_pdg) + " Hits: " + std::to_string(m_caloHits.size()) + "\n");
1075 for (const Node *pChild : m_children)
1076 str += pChild->ToString(prefix + " ");
1077
1078 return str;
1079}
1080
1081//------------------------------------------------------------------------------------------------------------------------------------------
1082//------------------------------------------------------------------------------------------------------------------------------------------
1083
1084LArHierarchyHelper::MCMatches::MCMatches(const MCHierarchy::Node *pMCParticle) : m_pMCParticle{pMCParticle}
1085{
1086}
1087
1088//------------------------------------------------------------------------------------------------------------------------------------------
1089
1091{
1092 m_recoNodes.emplace_back(pReco);
1093 m_sharedHits.emplace_back(nSharedHits);
1094}
1095
1096//------------------------------------------------------------------------------------------------------------------------------------------
1097
1099{
1100 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1101 if (iter == m_recoNodes.end())
1102 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1103 int index = iter - m_recoNodes.begin();
1104
1105 return static_cast<int>(m_sharedHits[index]);
1106}
1107
1108//------------------------------------------------------------------------------------------------------------------------------------------
1109
1110float LArHierarchyHelper::MCMatches::GetPurity(const RecoHierarchy::Node *pReco, const bool adcWeighted) const
1111{
1112 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1113 if (iter == m_recoNodes.end())
1114 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1115
1116 const CaloHitList &recoHits{pReco->GetCaloHits()};
1117 const CaloHitList &mcHits{m_pMCParticle->GetCaloHits()};
1118 CaloHitVector intersection;
1119 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1120
1121 return this->GetPurity(intersection, recoHits, adcWeighted);
1122}
1123
1124//------------------------------------------------------------------------------------------------------------------------------------------
1125
1126float LArHierarchyHelper::MCMatches::GetPurity(const RecoHierarchy::Node *pReco, const HitType view, const bool adcWeighted) const
1127{
1128 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1129 if (iter == m_recoNodes.end())
1130 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1131
1132 CaloHitList recoHits;
1133 for (const CaloHit *pCaloHit : pReco->GetCaloHits())
1134 if (pCaloHit->GetHitType() == view)
1135 recoHits.emplace_back(pCaloHit);
1136 CaloHitList mcHits;
1137 for (const CaloHit *pCaloHit : m_pMCParticle->GetCaloHits())
1138 if (pCaloHit->GetHitType() == view)
1139 mcHits.emplace_back(pCaloHit);
1140
1141 CaloHitVector intersection;
1142 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1143
1144 return this->GetPurity(intersection, recoHits, adcWeighted);
1145}
1146
1147//------------------------------------------------------------------------------------------------------------------------------------------
1148
1149float LArHierarchyHelper::MCMatches::GetCompleteness(const RecoHierarchy::Node *pReco, const bool adcWeighted) const
1150{
1151 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1152 if (iter == m_recoNodes.end())
1153 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1154
1155 const CaloHitList &recoHits{pReco->GetCaloHits()};
1156 const CaloHitList &mcHits{m_pMCParticle->GetCaloHits()};
1157 CaloHitVector intersection;
1158 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1159
1160 return this->GetCompleteness(intersection, mcHits, adcWeighted);
1161}
1162
1163//------------------------------------------------------------------------------------------------------------------------------------------
1164
1165float LArHierarchyHelper::MCMatches::GetCompleteness(const RecoHierarchy::Node *pReco, const HitType view, const bool adcWeighted) const
1166{
1167 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1168 if (iter == m_recoNodes.end())
1169 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1170
1171 CaloHitList recoHits;
1172 for (const CaloHit *pCaloHit : pReco->GetCaloHits())
1173 if (pCaloHit->GetHitType() == view)
1174 recoHits.emplace_back(pCaloHit);
1175 CaloHitList mcHits;
1176 for (const CaloHit *pCaloHit : m_pMCParticle->GetCaloHits())
1177 if (pCaloHit->GetHitType() == view)
1178 mcHits.emplace_back(pCaloHit);
1179
1180 CaloHitVector intersection;
1181 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1182
1183 return this->GetCompleteness(intersection, mcHits, adcWeighted);
1184}
1185
1186//------------------------------------------------------------------------------------------------------------------------------------------
1187
1188float LArHierarchyHelper::MCMatches::GetPurity(const CaloHitVector &intersection, const CaloHitList &recoHits, const bool adcWeighted) const
1189{
1190 float purity{0.f};
1191 if (!intersection.empty())
1192 {
1193 if (adcWeighted)
1194 {
1195 float adcSum{0.f};
1196 for (const CaloHit *pCaloHit : recoHits)
1197 adcSum += pCaloHit->GetInputEnergy();
1198 if (adcSum > std::numeric_limits<float>::epsilon())
1199 {
1200 for (const CaloHit *pCaloHit : intersection)
1201 purity += pCaloHit->GetInputEnergy();
1202 purity /= adcSum;
1203 }
1204 }
1205 else
1206 {
1207 purity = intersection.size() / static_cast<float>(recoHits.size());
1208 }
1209 }
1210
1211 return purity;
1212}
1213
1214//------------------------------------------------------------------------------------------------------------------------------------------
1215
1216float LArHierarchyHelper::MCMatches::GetCompleteness(const CaloHitVector &intersection, const CaloHitList &mcHits, const bool adcWeighted) const
1217{
1218 float completeness{0.f};
1219 if (!intersection.empty())
1220 {
1221 if (adcWeighted)
1222 {
1223 float adcSum{0.f};
1224 for (const CaloHit *pCaloHit : mcHits)
1225 adcSum += pCaloHit->GetInputEnergy();
1226 if (adcSum > std::numeric_limits<float>::epsilon())
1227 {
1228 for (const CaloHit *pCaloHit : intersection)
1229 completeness += pCaloHit->GetInputEnergy();
1230 completeness /= adcSum;
1231 }
1232 }
1233 else
1234 {
1235 completeness = intersection.size() / static_cast<float>(mcHits.size());
1236 }
1237 }
1238
1239 return completeness;
1240}
1241
1242//------------------------------------------------------------------------------------------------------------------------------------------
1243
1245{
1246 if (m_recoNodes.empty())
1247 return false;
1248
1249 int nAboveThreshold{0};
1250 if (m_recoNodes.size() != 1)
1251 {
1252 for (const LArHierarchyHelper::RecoHierarchy::Node *const pNode : m_recoNodes)
1253 if (this->GetCompleteness(pNode) > 0.1f)
1254 ++nAboveThreshold;
1255 if (nAboveThreshold != 1)
1256 return false;
1257 }
1258
1259 if (this->GetPurity(m_recoNodes.front()) < qualityCuts.m_minPurity)
1260 return false;
1261
1262 if (this->GetCompleteness(m_recoNodes.front()) < qualityCuts.m_minCompleteness)
1263 return false;
1264
1265 return true;
1266}
1267
1268//------------------------------------------------------------------------------------------------------------------------------------------
1269//------------------------------------------------------------------------------------------------------------------------------------------
1270
1271LArHierarchyHelper::MatchInfo::MatchInfo(const MCHierarchy &mcHierarchy, const RecoHierarchy &recoHierarchy) :
1272 MatchInfo(mcHierarchy, recoHierarchy, QualityCuts())
1273{
1274}
1275
1276//------------------------------------------------------------------------------------------------------------------------------------------
1277
1278LArHierarchyHelper::MatchInfo::MatchInfo(const MCHierarchy &mcHierarchy, const RecoHierarchy &recoHierarchy, const QualityCuts &qualityCuts) :
1279 m_mcHierarchy{mcHierarchy},
1280 m_recoHierarchy{recoHierarchy},
1281 m_qualityCuts{qualityCuts}
1282{
1283}
1284
1285//------------------------------------------------------------------------------------------------------------------------------------------
1286
1288{
1289 MCParticleList rootMCParticles;
1290 m_mcHierarchy.GetRootMCParticles(rootMCParticles);
1291 PfoList rootPfos;
1292 m_recoHierarchy.GetRootPfos(rootPfos);
1293 std::map<const MCHierarchy::Node *, MCMatches> mcToMatchMap;
1294
1295 for (const MCParticle *const pRootMC : rootMCParticles)
1296 {
1298 m_mcHierarchy.GetFlattenedNodes(pRootMC, mcNodes);
1299 for (const ParticleFlowObject *const pRootPfo : rootPfos)
1300 {
1301 RecoHierarchy::NodeVector recoNodes;
1302 m_recoHierarchy.GetFlattenedNodes(pRootPfo, recoNodes);
1303
1304 std::sort(mcNodes.begin(), mcNodes.end(), [](const MCHierarchy::Node *lhs, const MCHierarchy::Node *rhs) {
1305 return lhs->GetCaloHits().size() > rhs->GetCaloHits().size();
1306 });
1307 std::sort(recoNodes.begin(), recoNodes.end(), [](const RecoHierarchy::Node *lhs, const RecoHierarchy::Node *rhs) {
1308 return lhs->GetCaloHits().size() > rhs->GetCaloHits().size();
1309 });
1310
1311 for (const RecoHierarchy::Node *pRecoNode : recoNodes)
1312 {
1313 const CaloHitList &recoHits{pRecoNode->GetCaloHits()};
1314 const MCHierarchy::Node *pBestNode{nullptr};
1315 size_t bestSharedHits{0};
1316 for (const MCHierarchy::Node *pMCNode : mcNodes)
1317 {
1318 if (!pMCNode->IsReconstructable())
1319 continue;
1320 const CaloHitList &mcHits{pMCNode->GetCaloHits()};
1321 CaloHitVector intersection;
1322 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1323
1324 if (!intersection.empty())
1325 {
1326 const size_t sharedHits{intersection.size()};
1327 if (sharedHits > bestSharedHits)
1328 {
1329 bestSharedHits = sharedHits;
1330 pBestNode = pMCNode;
1331 }
1332 }
1333 }
1334 if (pBestNode)
1335 {
1336 auto iter{mcToMatchMap.find(pBestNode)};
1337 if (iter != mcToMatchMap.end())
1338 {
1339 MCMatches &match(iter->second);
1340 match.AddRecoMatch(pRecoNode, static_cast<int>(bestSharedHits));
1341 }
1342 else
1343 {
1344 MCMatches match(pBestNode);
1345 match.AddRecoMatch(pRecoNode, static_cast<int>(bestSharedHits));
1346 mcToMatchMap.insert(std::make_pair(pBestNode, match));
1347 }
1348 }
1349 else
1350 {
1351 m_unmatchedReco.emplace_back(pRecoNode);
1352 }
1353 }
1354 }
1355 }
1356
1357 for (auto [pMCNode, matches] : mcToMatchMap)
1358 {
1359 // We need to figure out which MC interaction hierarchy the matches belongs to
1360 for (const MCParticle *const pRootMC : rootMCParticles)
1361 {
1363 m_mcHierarchy.GetFlattenedNodes(pRootMC, mcNodes);
1364 if (std::find(mcNodes.begin(), mcNodes.end(), pMCNode) != mcNodes.end())
1365 {
1366 m_matches[pRootMC].emplace_back(matches);
1367 break;
1368 }
1369 }
1370 }
1371
1372 const auto predicate = [](const MCMatches &lhs, const MCMatches &rhs) {
1373 return lhs.GetMC()->GetCaloHits().size() > rhs.GetMC()->GetCaloHits().size();
1374 };
1375
1376 for (const MCParticle *const pRootMC : rootMCParticles)
1377 {
1378 std::sort(m_matches[pRootMC].begin(), m_matches[pRootMC].end(), predicate);
1379
1381 m_mcHierarchy.GetFlattenedNodes(pRootMC, mcNodes);
1382
1383 for (const MCHierarchy::Node *pMCNode : mcNodes)
1384 {
1385 if (pMCNode->IsReconstructable() && mcToMatchMap.find(pMCNode) == mcToMatchMap.end())
1386 {
1387 MCMatches match(pMCNode);
1388 m_matches[pRootMC].emplace_back(match);
1389 }
1390 }
1391 }
1392}
1393
1394//------------------------------------------------------------------------------------------------------------------------------------------
1395
1396unsigned int LArHierarchyHelper::MatchInfo::GetNMCNodes(const MCParticle *const pRoot) const
1397{
1398 if (m_matches.find(pRoot) == m_matches.end())
1399 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1400
1401 return static_cast<unsigned int>(m_matches.at(pRoot).size());
1402}
1403
1404//------------------------------------------------------------------------------------------------------------------------------------------
1405
1407{
1408 if (m_matches.find(pRoot) == m_matches.end())
1409 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1410
1411 unsigned int nNodes{0};
1412 for (const MCMatches &match : m_matches.at(pRoot))
1413 {
1414 const MCHierarchy::Node *pNode{match.GetMC()};
1415 if (!(pNode->IsCosmicRay() || pNode->IsTestBeamParticle()))
1416 ++nNodes;
1417 }
1418
1419 return nNodes;
1420}
1421
1422//------------------------------------------------------------------------------------------------------------------------------------------
1423
1425{
1426 if (m_matches.find(pRoot) == m_matches.end())
1427 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1428
1429 unsigned int nNodes{0};
1430 for (const MCMatches &match : m_matches.at(pRoot))
1431 {
1432 const MCHierarchy::Node *pNode{match.GetMC()};
1433 if (pNode->IsCosmicRay())
1434 ++nNodes;
1435 }
1436
1437 return nNodes;
1438}
1439
1440//------------------------------------------------------------------------------------------------------------------------------------------
1441
1443{
1444 if (m_matches.find(pRoot) == m_matches.end())
1445 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1446
1447 unsigned int nNodes{0};
1448 for (const MCMatches &match : m_matches.at(pRoot))
1449 {
1450 const MCHierarchy::Node *pNode{match.GetMC()};
1451 if (pNode->IsTestBeamParticle())
1452 ++nNodes;
1453 }
1454
1455 return nNodes;
1456}
1457
1458//------------------------------------------------------------------------------------------------------------------------------------------
1459
1461{
1462 (void)mcHierarchy;
1463 MCParticleList rootMCParticles;
1464 mcHierarchy.GetRootMCParticles(rootMCParticles);
1465
1466 for (const MCParticle *const pRootMC : rootMCParticles)
1467 {
1468 const LArHierarchyHelper::MCMatchesVector &matches{this->GetMatches(pRootMC)};
1469
1470 MCParticleList primaries;
1471 for (const LArHierarchyHelper::MCMatches &match : matches)
1472 {
1473 const LArHierarchyHelper::MCHierarchy::Node *pMCNode{match.GetMC()};
1474 if (pMCNode->GetHierarchyTier() == 1)
1475 {
1476 const MCParticle *const pLeadingMC{pMCNode->GetLeadingMCParticle()};
1477 primaries.emplace_back(pLeadingMC);
1478 }
1479 }
1482
1483 const LArMCParticle *const pLArRoot{dynamic_cast<const LArMCParticle *const>(pRootMC)};
1484 if (pLArRoot)
1485 std::cout << "=== MC Interaction : PDG " << std::to_string(pLArRoot->GetParticleId())
1486 << " Energy: " << std::to_string(pLArRoot->GetEnergy()) << " Type: " << descriptor.ToString() << std::endl;
1487 else
1488 std::cout << "=== MC Interaction : PDG " << std::to_string(pRootMC->GetParticleId())
1489 << " Energy: " << std::to_string(pRootMC->GetEnergy()) << " Type: " << descriptor.ToString() << std::endl;
1490
1491 unsigned int nNeutrinoMCParticles{this->GetNNeutrinoMCNodes(pRootMC)}, nNeutrinoRecoParticles{0};
1492 unsigned int nCosmicMCParticles{this->GetNCosmicRayMCNodes(pRootMC)}, nCosmicRecoParticles{0};
1493 unsigned int nTestBeamMCParticles{this->GetNTestBeamMCNodes(pRootMC)}, nTestBeamRecoParticles{0};
1494 std::cout << " === Matches ===" << std::endl;
1495 std::cout << std::fixed << std::setprecision(2);
1496 for (const MCMatches &match : m_matches.at(pRootMC))
1497 {
1498 const MCHierarchy::Node *pMCNode{match.GetMC()};
1499 const int pdg{pMCNode->GetParticleId()};
1500 const size_t mcHits{pMCNode->GetCaloHits().size()};
1501 const std::string tag{pMCNode->IsTestBeamParticle() ? "(Beam) " : pMCNode->IsCosmicRay() ? "(Cosmic) " : ""};
1502 std::cout << " MC " << tag << pdg << " hits " << mcHits << std::endl;
1503 const RecoHierarchy::NodeVector &nodeVector{match.GetRecoMatches()};
1504
1505 for (const RecoHierarchy::Node *pRecoNode : nodeVector)
1506 {
1507 const unsigned int recoHits{static_cast<unsigned int>(pRecoNode->GetCaloHits().size())};
1508 const unsigned int sharedHits{match.GetSharedHits(pRecoNode)};
1509 const float purity{match.GetPurity(pRecoNode)};
1510 const float completeness{match.GetCompleteness(pRecoNode)};
1511 if (completeness > 0.1f)
1512 std::cout << " Matched " << sharedHits << " out of " << recoHits << " with purity " << purity << " and completeness "
1513 << completeness << std::endl;
1514 else
1515 std::cout << " (Below threshold) " << sharedHits << " out of " << recoHits << " with purity " << purity
1516 << " and completeness " << completeness << std::endl;
1517 }
1518 if (nodeVector.empty())
1519 {
1520 std::cout << " Unmatched" << std::endl;
1521 }
1522 else if (match.IsQuality(this->GetQualityCuts()))
1523 {
1524 if (pMCNode->IsTestBeamParticle())
1525 ++nTestBeamRecoParticles;
1526 else if (pMCNode->IsCosmicRay())
1527 ++nCosmicRecoParticles;
1528 else
1529 ++nNeutrinoRecoParticles;
1530 }
1531 }
1532
1534 {
1535 std::cout << " Neutrino Interaction Summary:" << std::endl;
1536 if (nNeutrinoMCParticles)
1537 {
1538 std::cout << " Good final state particles: " << nNeutrinoRecoParticles << " of " << nNeutrinoMCParticles << " : "
1539 << (100 * nNeutrinoRecoParticles / static_cast<float>(nNeutrinoMCParticles)) << "%" << std::endl;
1540 }
1541 }
1542 else if (LArMCParticleHelper::IsCosmicRay(pRootMC))
1543 {
1544 std::cout << " Cosmic Ray Interaction Summary:" << std::endl;
1545 std::cout << std::fixed << std::setprecision(1);
1546 if (nCosmicMCParticles)
1547 {
1548 std::cout << " Good cosmics: " << nCosmicRecoParticles << " of " << nCosmicMCParticles << " : "
1549 << (100 * nCosmicRecoParticles / static_cast<float>(nCosmicMCParticles)) << "%" << std::endl;
1550 }
1551 }
1552 else if (LArMCParticleHelper::IsBeamParticle(pRootMC))
1553 {
1554 std::cout << " Test Beam Interaction Summary:" << std::endl;
1555 std::cout << std::fixed << std::setprecision(1);
1556 if (nTestBeamMCParticles)
1557 {
1558 std::cout << " Good test beam particles: " << nTestBeamRecoParticles << " of " << nTestBeamMCParticles << " : "
1559 << (100 * nTestBeamRecoParticles / static_cast<float>(nTestBeamMCParticles)) << "%" << std::endl;
1560 }
1561 if (nCosmicMCParticles)
1562 {
1563 std::cout << " Matched cosmics: " << nCosmicRecoParticles << " of " << nCosmicMCParticles << " : "
1564 << (100 * nCosmicRecoParticles / static_cast<float>(nCosmicMCParticles)) << "%" << std::endl;
1565 }
1566 }
1567 if (!this->GetUnmatchedReco().empty())
1568 std::cout << " Unmatched reco: " << this->GetUnmatchedReco().size() << std::endl;
1569 }
1570}
1571
1572//------------------------------------------------------------------------------------------------------------------------------------------
1573
1575{
1576 for (auto iter = m_matches.begin(); iter != m_matches.end(); ++iter)
1577 rootMCParticles.emplace_back(iter->first);
1578}
1579
1580//------------------------------------------------------------------------------------------------------------------------------------------
1581//------------------------------------------------------------------------------------------------------------------------------------------
1582
1584 const MCParticleList &mcParticleList, const CaloHitList &caloHitList, const FoldingParameters &foldParameters, MCHierarchy &hierarchy)
1585{
1586 hierarchy.FillHierarchy(mcParticleList, caloHitList, foldParameters);
1587}
1588
1589//------------------------------------------------------------------------------------------------------------------------------------------
1590
1591void LArHierarchyHelper::FillRecoHierarchy(const PfoList &pfoList, const FoldingParameters &foldParameters, RecoHierarchy &hierarchy)
1592{
1593 hierarchy.FillHierarchy(pfoList, foldParameters);
1594}
1595
1596//------------------------------------------------------------------------------------------------------------------------------------------
1597
1599{
1600 matchInfo.Match();
1601}
1602
1603//------------------------------------------------------------------------------------------------------------------------------------------
1604// private
1605
1607{
1608 try
1609 {
1610 MCParticleList visible;
1612 // Check if we still need to use sets here
1613 for (const MCParticle *const pMCParticle : visible)
1614 primaries.insert(pMCParticle);
1615 }
1616 catch (const StatusCodeException &)
1617 {
1618 if (pRoot->GetParticleId() != 111 && pRoot->GetParticleId() < 1e9)
1619 std::cout << "LArHierarchyHelper::MCHierarchy::FillHierarchy: MC particle with PDG code " << pRoot->GetParticleId()
1620 << " at address " << pRoot << " has no associated primary particle" << std::endl;
1621 }
1622}
1623
1625{
1626 if (LArPfoHelper::IsNeutrino(pRoot))
1627 {
1628 const PfoList &children{pRoot->GetDaughterPfoList()};
1629 // Check if we still need to use sets here
1630 for (const ParticleFlowObject *const pPfo : children)
1631 primaries.insert(pPfo);
1632 }
1633 else
1634 {
1635 // Might want different handling here for test beam and cosmic ray particles, but for now, just treat
1636 // a non-neutrino root node as something to be stored in the primaries set directly
1637 primaries.insert(pRoot);
1638 }
1639}
1640
1641} // namespace lar_content
Header file for the lar hierarchy helper class.
Header file for the interaction type helper class.
Header file defining status codes and relevant preprocessor macros.
@ NEUTRON
Definition Validation.h:213
bool m_foldToTier
Whether or not to apply folding based on particle tier.
bool m_foldDynamic
Whether or not to use process and topological information to make folding decisions.
float m_cosAngleTolerance
Cosine of the maximum angle at which topologies can be considered continuous.
int m_tier
If folding to a tier, the tier to be combined with its child particles.
bool m_foldToLeadingShowers
Whether or not to fold shower children to the leading shower particle.
const std::string ToString(const std::string &prefix) const
Produce a string representation of the hierarchy.
bool IsTestBeamParticle() const
Check if this is a test beam particle.
const pandora::CaloHitList & GetCaloHits() const
Retrieve the CaloHits associated with this node.
void FillFlat(const pandora::MCParticle *pRoot)
Fill this node by folding all descendent particles to this node.
void SetLeadingLepton()
Tags the particle as the leading lepton.
void FillHierarchy(const pandora::MCParticle *pRoot, const FoldingParameters &foldParameters)
Recursively fill the hierarchy based on the criteria established for this MCHierarchy.
MCHierarchy & m_hierarchy
The parent MC hierarchy.
pandora::MCParticleList m_mcParticles
The list of MC particles of which this node is composed.
Node(MCHierarchy &hierarchy, const pandora::MCParticle *pMCParticle, const int tier=1)
Create a node with a primary MC particle.
bool IsReconstructable() const
Return whether or not this node should be considered reconstructable.
const pandora::MCParticle * m_mainParticle
The leading MC particle for this node.
int GetId() const
Retrieve the unique ID of this node.
int m_pdg
The PDG code of the leading MC particle for this node.
int GetParticleId() const
Retrieve the PDG code for the leading particle in this node.
pandora::CaloHitList m_caloHits
The list of calo hits of which this node is composed.
bool IsCosmicRay() const
Check if this is a cosmic ray particle.
const NodeVector & GetInteractions(const pandora::MCParticle *pRoot) const
Retrieve the root nodes in this hierarchy.
void GetFlattenedNodes(const pandora::MCParticle *const pRoot, NodeVector &nodeVector) const
Retrieve a flat vector of the ndoes in the hierarchy.
const std::string ToString() const
Produce a string representation of the hierarchy.
void InterpretHierarchy(const pandora::MCParticle *const pRoot, pandora::MCParticleList &leadingParticles, pandora::MCParticleList &childParticles, const float cosAngleTolerance) const
Interpret the hierarchy below a particular particle to determine if and how it should be folded....
void RegisterNode(const Node *pNode)
Register a node with the hierarchy.
void CollectContinuations(const pandora::MCParticle *pRoot, pandora::MCParticleList &continuingParticles, pandora::MCParticleList &childParticles, const float cosAngleTolerance) const
Identify downstream particles that represent continuations of the parent particle from a reconstructi...
void GetRootMCParticles(pandora::MCParticleList &rootMCParticles) const
Retrieve the root MC particles of the interaction hierarchies.
bool IsReconstructable(const pandora::MCParticle *pMCParticle) const
Checks if an individual particle meets reconstructability criteria.
MCNodeVectorMap m_interactions
Map from incident particles (e.g. neutrino) to primaries.
void FillHierarchy(const pandora::MCParticleList &mcParticleList, const pandora::CaloHitList &caloHitList, const FoldingParameters &foldParameters)
Creates an MC hierarchy representation. Without folding this will be a mirror image of the standard M...
bool IsQuality(const QualityCuts &qualityCuts) const
Get whether this match passes quality cuts.
MCMatches(const MCHierarchy::Node *pMCParticle)
Constructor.
void AddRecoMatch(const RecoHierarchy::Node *pReco, const int nSharedHits)
Add a reconstructed node as a match for this MC node.
float GetCompleteness(const RecoHierarchy::Node *pReco, const bool adcWeighted=false) const
Retrieve the completeness of the match.
const MCHierarchy::Node * GetMC() const
Retrieve the MC node.
unsigned int GetSharedHits(const RecoHierarchy::Node *pReco) const
Retrieve the number of shared hits in the match.
float GetPurity(const RecoHierarchy::Node *pReco, const bool adcWeighted=false) const
Retrieve the purity of the match.
void Print(const MCHierarchy &mcHierarchy) const
Prints information about which reco nodes are matched to the MC nodes, information about hit sharing,...
void GetRootMCParticles(pandora::MCParticleList &rootMCParticles) const
Retrieve the root MC particles of the interaction hierarchies.
unsigned int GetNMCNodes(const pandora::MCParticle *const pRoot) const
Retrieve the number of MC nodes available to match.
unsigned int GetNTestBeamMCNodes(const pandora::MCParticle *const pRoot) const
Retrieve the number of test beam derived MC nodes available to match.
MatchInfo(const MCHierarchy &mcHierarchy, const RecoHierarchy &recoHierarchy)
Default constructor.
unsigned int GetNCosmicRayMCNodes(const pandora::MCParticle *const pRoot) const
Retrieve the number of cosmic ray derived MC nodes available to match.
unsigned int GetNNeutrinoMCNodes(const pandora::MCParticle *const pRoot) const
Retrieve the number of neutrino interaction derived MC nodes available to match.
void Match()
Match the nodes in the MC and reco hierarchies.
const float m_minPurity
The minimum purity for a match to be considered good.
const float m_minCompleteness
The minimum completeness for a match to be considered good.
void FillFlat(const pandora::ParticleFlowObject *pRoot)
Fill this node by folding all descendent particles to this node.
void FillHierarchy(const pandora::ParticleFlowObject *pRoot, const FoldingParameters &foldParameters)
Recursively fill the hierarchy based on the criteria established for this RecoHierarchy.
pandora::CaloHitList m_caloHits
The list of calo hits of which this node is composed.
const pandora::ParticleFlowObject * m_mainPfo
The leading particle flow object for this node.
pandora::PfoList m_pfos
The list of PFOs of which this node is composed.
const pandora::PfoList & GetRecoParticles() const
Retrieve the PFOs associated with this node.
const pandora::CaloHitList & GetCaloHits() const
Retrieve the CaloHits associated with this node.
int m_pdg
The particle ID (track = muon, shower = electron)
const std::string ToString(const std::string &prefix) const
Produce a string representation of the hierarchy.
int GetParticleId() const
Retrieve the PDG code for the leading particle in this node Note, for reco objects the PDG codes repr...
Node(const RecoHierarchy &hierarchy, const pandora::ParticleFlowObject *pPfo)
Create a node with a primary PFO.
void FillHierarchy(const pandora::PfoList &pfoList, const FoldingParameters &foldParameters)
Creates a reconstructed hierarchy representation. Without folding this will be a mirror image of the ...
const NodeVector & GetInteractions(const pandora::ParticleFlowObject *pRoot) const
Retrieve the root nodes in the hierarchy for a given interaction.
void GetRootPfos(pandora::PfoList &rootPfos) const
Retrieve the root particle flow objects of the interaction hierarchies.
void GetFlattenedNodes(const pandora::ParticleFlowObject *const pRoot, NodeVector &nodeVector) const
Retrieve a flat vector of the nodes in the hierarchy.
const std::string ToString() const
Produce a string representation of the hierarchy.
static void GetRecoPrimaries(const pandora::ParticleFlowObject *pRoot, PfoSet &primaries)
Retrieves the primary PFOs from a list and returns the root (neutrino) for hierarchy,...
static void GetMCPrimaries(const pandora::MCParticle *pRoot, MCParticleSet &primaries)
Retrieves the primary MC particles from a list and returns the root (neutrino) for hierarchy,...
std::set< const pandora::MCParticle * > MCParticleSet
static void MatchHierarchies(MatchInfo &matchInfo)
Finds the matches between reconstructed and MC hierarchies.
static void FillRecoHierarchy(const pandora::PfoList &pfoList, const FoldingParameters &foldParameters, RecoHierarchy &hierarchy)
Fill a reconstructed hierarchy based on the specified folding criteria (see RecoHierarchy::FillHierar...
std::vector< MCMatches > MCMatchesVector
static void FillMCHierarchy(const pandora::MCParticleList &mcParticleList, const pandora::CaloHitList &caloHitList, const FoldingParameters &foldParameters, MCHierarchy &hierarchy)
Fill an MC hierarchy based on the specified folding criteria (see MCHierarchy::FillHierarchy for deta...
std::set< const pandora::ParticleFlowObject * > PfoSet
static InteractionDescriptor GetInteractionDescriptor(const pandora::MCParticleList &mcPrimaryList)
Get the interaction descriptor of an event.
static bool IsCosmicRay(const pandora::MCParticle *const pMCParticle)
Return true if passed a primary cosmic ray MCParticle.
static bool SortByMomentum(const pandora::MCParticle *const pLhs, const pandora::MCParticle *const pRhs)
Sort mc particles by their momentum.
static bool IsInelasticScatter(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle came from an inelastic scattering process.
static void GetFirstVisibleMCParticles(const pandora::MCParticle *const pRoot, pandora::MCParticleList &visibleParticleList)
Get the first visible MC particles given a root particle. For example, given a neutrino this would re...
static void GetAllDescendentMCParticles(const pandora::MCParticle *const pMCParticle, pandora::MCParticleList &descendentMCParticleList)
Get all descendent mc particles.
static bool IsNeutrino(const pandora::MCParticle *const pMCParticle)
Whether a mc particle is a neutrino or antineutrino.
static bool IsBeamParticle(const pandora::MCParticle *const pMCParticle)
Returns true if passed a primary beam MCParticle.
static bool AreTopologicallyContinuous(const pandora::MCParticle *const pMCParent, const pandora::MCParticle *const pMCChild, const float cosAngleTolerance)
Determine if two MC particles are topologically continuous within a given tolerance....
static bool IsElasticScatter(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle came from an elastic scattering process.
static int GetHierarchyTier(const pandora::MCParticle *const pMCParticle)
Determine the position in the hierarchy for the MCParticle.
LAr mc particle class.
static void GetAllDownstreamPfos(const pandora::PfoList &inputPfoList, pandora::PfoList &outputPfoList)
Get a flat list of all pfos, recursively, of all daughters associated with those pfos in an input lis...
static void GetAllCaloHits(const pandora::ParticleFlowObject *pPfo, pandora::CaloHitList &caloHitList)
Get a list of all calo hits (including isolated) of all types from a given pfo.
static bool IsNeutrino(const pandora::ParticleFlowObject *const pPfo)
Whether a pfo is a neutrino or (antineutrino)
static bool SortByNHits(const pandora::ParticleFlowObject *const pLhs, const pandora::ParticleFlowObject *const pRhs)
Sort pfos by number of constituent hits.
static int GetHierarchyTier(const pandora::ParticleFlowObject *const pPfo)
Determine the position in the hierarchy for the MCParticle.
CaloHit class.
Definition CaloHit.h:26
float GetMagnitude() const
Get the magnitude.
static const MCParticle * GetMainMCParticle(const T *const pT)
Find the mc particle making the largest contribution to a specified calo hit, track or cluster.
MCParticle class.
Definition MCParticle.h:26
const MCParticleList & GetDaughterList() const
Get list of daughters of mc particle.
Definition MCParticle.h:306
const CartesianVector & GetMomentum() const
Get momentum of mc particle, units GeV.
Definition MCParticle.h:250
const MCParticleList & GetParentList() const
Get list of parents of mc particle.
Definition MCParticle.h:299
int GetParticleId() const
Get the PDG code of the mc particle.
Definition MCParticle.h:285
ParticleFlowObject class.
const PfoList & GetDaughterPfoList() const
Get the daughter pfo list.
int GetParticleId() const
Get the particle flow object id (PDG code)
StatusCodeException class.
HitType
Calorimeter hit type enum.
MANAGED_CONTAINER< const MCParticle * > MCParticleList
std::vector< const CaloHit * > CaloHitVector
MANAGED_CONTAINER< const CaloHit * > CaloHitList
MANAGED_CONTAINER< const ParticleFlowObject * > PfoList