9 #include "Pandora/PdgTable.h"
10 #include "Pandora/StatusCodes.h"
22 m_foldToLeadingShowers{
false},
25 m_cosAngleTolerance{0.9962f},
33 m_foldToLeadingShowers{
false},
35 m_foldDynamic{foldDynamic},
36 m_cosAngleTolerance{cosAngleTolerance},
44 m_foldToLeadingShowers{
false},
47 m_cosAngleTolerance{0.9962f},
52 std::cout <<
"LArHierarchyHelper: Error - attempting to fold to non-positive tier" << std::endl;
53 throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
67 m_minPurity{minPurity},
68 m_minCompleteness{minCompleteness}
82 m_recoCriteria(recoCriteria),
92 for (
const Node *pNode : m_rootNodes)
101 const auto predicate = [](
const MCParticle *pMCParticle) {
return std::abs(pMCParticle->GetParticleId()) == NEUTRON; };
102 m_mcToHitsMap.clear();
103 for (
const CaloHit *pCaloHit : caloHitList)
107 const MCParticle *pMCParticle{MCParticleHelper::GetMainMCParticle(pCaloHit)};
108 m_mcToHitsMap[pMCParticle].emplace_back(pCaloHit);
110 catch (
const StatusCodeException &)
117 MCParticleList primaries(primarySet.begin(), primarySet.end());
119 if (m_recoCriteria.m_removeNeutrons)
120 primaries.erase(std::remove_if(primaries.begin(), primaries.end(), predicate), primaries.end());
123 for (
const MCParticle *pPrimary : primaries)
125 MCParticleList allParticles{pPrimary};
126 if (!m_recoCriteria.m_removeNeutrons)
133 MCParticleList dummy;
137 for (
const MCParticle *pMCParticle : allParticles)
140 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
142 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
143 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
146 m_rootNodes.emplace_back(
new Node(*
this, allParticles, allHits));
151 for (
const MCParticle *pPrimary : primaries)
153 MCParticleList allParticles{pPrimary};
155 const bool isShower{
pdg == E_MINUS ||
pdg == PHOTON};
156 const bool isNeutron{
pdg == NEUTRON};
157 if (isShower || (isNeutron && !m_recoCriteria.m_removeNeutrons))
160 for (
const MCParticle *pMCParticle : allParticles)
163 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
165 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
166 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
169 Node *pNode{
new Node(*
this, allParticles, allHits)};
170 m_rootNodes.emplace_back(pNode);
171 if (!(isShower || isNeutron))
174 const MCParticleList &children{pPrimary->GetDaughterList()};
175 for (
const MCParticle *pChild : children)
182 for (
const MCParticle *pPrimary : primaries)
184 MCParticleList leadingParticles, childParticles;
185 this->InterpretHierarchy(pPrimary, leadingParticles, childParticles, foldParameters.
m_cosAngleTolerance);
187 for (
const MCParticle *pMCParticle : leadingParticles)
190 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
192 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
193 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
197 Node *pNode{
new Node(*
this, leadingParticles, allHits)};
198 m_rootNodes.emplace_back(pNode);
199 for (
const MCParticle *pChild : childParticles)
206 for (
const MCParticle *pPrimary : primaries)
208 MCParticleList allParticles{pPrimary};
210 for (
const MCParticle *pMCParticle : allParticles)
213 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
215 const CaloHitList &caloHits(m_mcToHitsMap.at(pMCParticle));
216 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
219 Node *pNode{
new Node(*
this, allParticles, allHits)};
220 m_rootNodes.emplace_back(pNode);
222 const MCParticleList &children{pPrimary->GetDaughterList()};
223 for (
const MCParticle *pChild : children)
228 Node *pLeadingLepton{
nullptr};
229 float leadingLeptonEnergy{-std::numeric_limits<float>::max()};
230 for (
const Node *pNode : m_rootNodes)
236 if ((
pdg == MU_MINUS ||
pdg == E_MINUS ||
pdg == TAU_MINUS) && pMC->GetEnergy() > leadingLeptonEnergy)
238 pLeadingLepton =
const_cast<Node *
>(pNode);
239 leadingLeptonEnergy = pMC->GetEnergy();
250 const MCParticle *
const pRoot, MCParticleList &leadingParticles, MCParticleList &childParticles,
const float cosAngleTolerance)
const
252 leadingParticles.emplace_back(pRoot);
253 MCParticleList foldCandidates, childCandidates;
254 const MCParticleList &children{pRoot->GetDaughterList()};
255 for (
const MCParticle *pMCParticle : children)
264 if (pMCParticle->GetParticleId() == pRoot->GetParticleId())
265 foldCandidates.emplace_back(pMCParticle);
267 childCandidates.emplace_back(pMCParticle);
269 else if (!m_recoCriteria.m_removeNeutrons || (m_recoCriteria.m_removeNeutrons && pLArMCParticle->GetProcess() !=
MC_PROC_N_CAPTURE))
272 childCandidates.emplace_back(pMCParticle);
275 const MCParticle *pBestFoldCandidate{
nullptr};
276 float bestDp{std::numeric_limits<float>::max()};
277 for (
const MCParticle *pMCParticle : foldCandidates)
279 if (foldCandidates.size() == 1)
284 pBestFoldCandidate = pMCParticle;
286 childCandidates.emplace_back(pMCParticle);
293 const float dp{pRoot->GetMomentum().GetMagnitude() - pMCParticle->GetMomentum().GetMagnitude()};
296 pBestFoldCandidate = pMCParticle;
302 childCandidates.emplace_back(pMCParticle);
306 if (pBestFoldCandidate)
308 leadingParticles.emplace_back(pBestFoldCandidate);
311 this->CollectContinuations(pBestFoldCandidate, leadingParticles, childCandidates, cosAngleTolerance);
313 for (
const MCParticle *pMCParticle : childCandidates)
316 if (this->IsReconstructable(pMCParticle))
317 childParticles.emplace_back(pMCParticle);
320 MCParticleList localHierarchy{pMCParticle};
321 CaloHitList localHits;
323 for (
const MCParticle *pLocalMCParticle : localHierarchy)
325 if (m_mcToHitsMap.find(pLocalMCParticle) != m_mcToHitsMap.end())
327 const CaloHitList &caloHits(m_mcToHitsMap.at(pLocalMCParticle));
328 localHits.insert(localHits.begin(), caloHits.begin(), caloHits.end());
331 if (this->IsReconstructable(localHits))
332 childParticles.emplace_back(pMCParticle);
340 const MCParticle *pRoot, MCParticleList &continuingParticles, MCParticleList &childParticles,
const float cosAngleTolerance)
const
342 const MCParticleList &children{pRoot->GetDaughterList()};
343 MCParticleList foldCandidates;
344 for (
const MCParticle *pMCParticle : children)
352 if (pMCParticle->GetParticleId() == pRoot->GetParticleId())
353 foldCandidates.emplace_back(pMCParticle);
355 else if (!m_recoCriteria.m_removeNeutrons || (m_recoCriteria.m_removeNeutrons && pLArMCParticle->GetProcess() !=
MC_PROC_N_CAPTURE))
358 childParticles.emplace_back(pMCParticle);
361 const MCParticle *pBestFoldCandidate{
nullptr};
362 float bestDp{std::numeric_limits<float>::max()};
363 for (
const MCParticle *pMCParticle : foldCandidates)
365 if (foldCandidates.size() == 1)
370 pBestFoldCandidate = pMCParticle;
377 const float dp{pRoot->GetMomentum().GetMagnitude() - pMCParticle->GetMomentum().GetMagnitude()};
380 pBestFoldCandidate = pMCParticle;
386 if (pBestFoldCandidate)
388 continuingParticles.emplace_back(pBestFoldCandidate);
389 const MCParticleList &newLeadingParticles{pBestFoldCandidate->GetDaughterList()};
391 childParticles.insert(childParticles.begin(), newLeadingParticles.begin(), newLeadingParticles.end());
393 const auto iter{std::find(childParticles.begin(), childParticles.end(), pBestFoldCandidate)};
394 if (iter != childParticles.end())
395 childParticles.erase(iter);
407 for (
const Node *pNode : m_rootNodes)
409 nodeVector.emplace_back(pNode);
410 queue.emplace_back(pNode);
412 while (!queue.empty())
414 const NodeVector &children{queue.front()->GetChildren()};
416 for (
const Node *pChild : children)
418 nodeVector.emplace_back(pChild);
419 queue.emplace_back(pChild);
428 m_nodeToIdMap.insert(std::make_pair(pNode, m_nextNodeId));
437 for (
const Node *pNode : m_rootNodes)
438 str += pNode->ToString(
"") +
"\n";
447 if (m_mcToHitsMap.find(pMCParticle) != m_mcToHitsMap.end())
449 unsigned int nHitsU{0}, nHitsV{0}, nHitsW{0};
450 for (
const CaloHit *pCaloHit : m_mcToHitsMap.at(pMCParticle))
452 const HitType view{pCaloHit->GetHitType()};
453 if (view == TPC_VIEW_U)
455 else if (view == TPC_VIEW_V)
457 else if (view == TPC_VIEW_W)
460 const unsigned int nHits{nHitsU + nHitsV + nHitsW};
461 unsigned int nGoodViews{0};
462 nGoodViews += nHitsU >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
463 nGoodViews += nHitsV >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
464 nGoodViews += nHitsW >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
466 return nHits >= m_recoCriteria.m_minHits && nGoodViews >= m_recoCriteria.m_minGoodViews;
476 unsigned int nHitsU{0}, nHitsV{0}, nHitsW{0};
477 for (
const CaloHit *pCaloHit : caloHits)
479 const HitType view{pCaloHit->GetHitType()};
480 if (view == TPC_VIEW_U)
482 else if (view == TPC_VIEW_V)
484 else if (view == TPC_VIEW_W)
487 const unsigned int nHits{nHitsU + nHitsV + nHitsW};
488 unsigned int nGoodViews{0};
489 nGoodViews += nHitsU >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
490 nGoodViews += nHitsV >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
491 nGoodViews += nHitsW >= m_recoCriteria.m_minHitsForGoodView ? 1 : 0;
493 return nHits >= m_recoCriteria.m_minHits && nGoodViews >= m_recoCriteria.m_minGoodViews;
500 m_hierarchy(hierarchy),
501 m_mainParticle(pMCParticle),
504 m_isLeadingLepton{
false}
508 m_pdg = pMCParticle->GetParticleId();
509 m_mcParticles.emplace_back(pMCParticle);
511 m_hierarchy.RegisterNode(
this);
517 m_hierarchy(hierarchy),
518 m_mcParticles(mcParticleList),
519 m_caloHits(caloHitList),
520 m_mainParticle(nullptr),
523 m_isLeadingLepton{
false}
525 if (!mcParticleList.empty())
527 m_mainParticle = mcParticleList.front();
528 m_pdg = m_mainParticle->GetParticleId();
532 m_hierarchy.RegisterNode(
this);
539 m_mcParticles.clear();
541 for (
const Node *node : m_children)
552 MCParticleList leadingParticles, childParticles;
553 m_hierarchy.InterpretHierarchy(pRoot, leadingParticles, childParticles, foldParameters.
m_cosAngleTolerance);
555 for (
const MCParticle *pMCParticle : leadingParticles)
558 if (m_hierarchy.m_mcToHitsMap.find(pMCParticle) != m_hierarchy.m_mcToHitsMap.end())
560 const CaloHitList &caloHits(m_hierarchy.m_mcToHitsMap.at(pMCParticle));
561 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
565 Node *pNode{
new Node(m_hierarchy, leadingParticles, allHits, this->m_tier + 1)};
566 m_children.emplace_back(pNode);
567 for (
const MCParticle *pChild : childParticles)
572 MCParticleList allParticles{pRoot};
574 const bool isShower{
pdg == E_MINUS ||
pdg == PHOTON};
575 const bool isNeutron{
pdg == NEUTRON};
579 else if (foldParameters.
m_foldToLeadingShowers && (isShower || (isNeutron && !m_hierarchy.m_recoCriteria.m_removeNeutrons)))
581 else if (m_hierarchy.m_recoCriteria.m_removeNeutrons && isNeutron)
585 for (
const MCParticle *pMCParticle : allParticles)
588 if (m_hierarchy.m_mcToHitsMap.find(pMCParticle) != m_hierarchy.m_mcToHitsMap.end())
590 const CaloHitList &caloHits(m_hierarchy.m_mcToHitsMap.at(pMCParticle));
591 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
595 if (!allParticles.empty())
601 if (hasChildren || (!hasChildren && !allHits.empty()))
603 Node *pNode{
new Node(m_hierarchy, allParticles, allHits, this->m_tier + 1)};
604 m_children.emplace_back(pNode);
608 const MCParticleList &children{pRoot->GetDaughterList()};
609 for (
const MCParticle *pChild : children)
621 MCParticleList allParticles{pRoot};
622 if (!m_hierarchy.m_recoCriteria.m_removeNeutrons)
628 MCParticleList neutrons;
632 for (
const MCParticle *pMCParticle : allParticles)
635 if (m_hierarchy.m_mcToHitsMap.find(pMCParticle) != m_hierarchy.m_mcToHitsMap.end())
637 const CaloHitList &caloHits(m_hierarchy.m_mcToHitsMap.at(pMCParticle));
638 allHits.insert(allHits.begin(), caloHits.begin(), caloHits.end());
641 if (!allParticles.empty())
643 Node *pNode{
new Node(m_hierarchy, allParticles, allHits, this->m_tier + 1)};
644 m_children.emplace_back(pNode);
652 return m_hierarchy.m_nodeToIdMap.at(
this);
659 const bool enoughHits{m_caloHits.size() >= m_hierarchy.m_recoCriteria.m_minHits};
662 bool enoughGoodViews{
false};
663 unsigned int nHitsU{0}, nHitsV{0}, nHitsW{0};
664 for (
const CaloHit *pCaloHit : m_caloHits)
666 switch (pCaloHit->GetHitType())
680 unsigned int nGoodViews{0};
681 if (nHitsU >= m_hierarchy.m_recoCriteria.m_minHitsForGoodView)
683 if (nHitsV >= m_hierarchy.m_recoCriteria.m_minHitsForGoodView)
685 if (nHitsW >= m_hierarchy.m_recoCriteria.m_minHitsForGoodView)
687 if (nGoodViews >= m_hierarchy.m_recoCriteria.m_minGoodViews)
689 enoughGoodViews =
true;
694 return enoughGoodViews;
721 std::string str(prefix +
"PDG: " +
std::to_string(m_pdg) +
" Energy: " +
std::to_string(m_mainParticle ? m_mainParticle->GetEnergy() : 0) +
723 for (
const Node *pChild : m_children)
724 str += pChild->ToString(prefix +
" ");
734 m_minHitsForGoodView{10},
736 m_removeNeutrons{
true}
744 m_minHitsForGoodView{obj.m_minHitsForGoodView},
745 m_minGoodViews{obj.m_minGoodViews},
746 m_removeNeutrons{obj.m_removeNeutrons}
753 const unsigned int minHits,
const unsigned int minHitsForGoodView,
const unsigned int minGoodViews,
const bool removeNeutrons) :
755 m_minHitsForGoodView{minHitsForGoodView},
756 m_minGoodViews{minGoodViews},
757 m_removeNeutrons{removeNeutrons}
772 for (
const Node *pNode : m_rootNodes)
783 PfoList primaries(primarySet.begin(), primarySet.end());
787 for (
const ParticleFlowObject *pPrimary : primaries)
789 PfoList allParticles;
793 for (
const ParticleFlowObject *pPfo : allParticles)
795 m_rootNodes.emplace_back(
new Node(*
this, allParticles, allHits));
800 for (
const ParticleFlowObject *pPrimary : primaries)
802 PfoList allParticles;
804 const bool isShower{
pdg == E_MINUS};
809 allParticles.emplace_back(pPrimary);
812 for (
const ParticleFlowObject *pPfo : allParticles)
814 Node *pNode{
new Node(*
this, allParticles, allHits)};
815 m_rootNodes.emplace_back(pNode);
819 const PfoList &children{pPrimary->GetDaughterPfoList()};
820 for (
const ParticleFlowObject *pChild : children)
828 for (
const ParticleFlowObject *pPrimary : primaries)
830 PfoList allParticles{pPrimary};
832 for (
const ParticleFlowObject *pPfo : allParticles)
834 Node *pNode{
new Node(*
this, allParticles, allHits)};
835 m_rootNodes.emplace_back(pNode);
837 const PfoList &children{pPrimary->GetDaughterPfoList()};
838 for (
const ParticleFlowObject *pChild : children)
849 for (
const Node *pNode : m_rootNodes)
851 nodeVector.emplace_back(pNode);
852 queue.emplace_back(pNode);
854 while (!queue.empty())
856 const NodeVector &children{queue.front()->GetChildren()};
858 for (
const Node *pChild : children)
860 nodeVector.emplace_back(pChild);
861 queue.emplace_back(pChild);
871 for (
const Node *pNode : m_rootNodes)
872 str += pNode->ToString(
"") +
"\n";
881 m_hierarchy(hierarchy),
886 m_pdg = pPfo->GetParticleId();
887 m_pfos.emplace_back(pPfo);
894 m_hierarchy(hierarchy),
897 if (!pfoList.empty())
898 m_pdg = pfoList.front()->GetParticleId();
901 m_caloHits = caloHitList;
911 for (
const Node *node : m_children)
920 PfoList allParticles;
922 const bool isShower{
pdg == E_MINUS};
928 allParticles.emplace_back(pRoot);
931 for (
const ParticleFlowObject *pPfo : allParticles)
937 if (hasChildren || (!hasChildren && !allHits.empty()))
939 Node *pNode{
new Node(m_hierarchy, allParticles, allHits)};
940 m_children.emplace_back(pNode);
944 const PfoList &children{pRoot->GetDaughterPfoList()};
945 for (
const ParticleFlowObject *pChild : children)
955 PfoList allParticles;
958 for (
const ParticleFlowObject *pPfo : allParticles)
960 Node *pNode{
new Node(m_hierarchy, allParticles, allHits)};
961 m_children.emplace_back(pNode);
990 for (
const Node *pChild : m_children)
991 str += pChild->ToString(prefix +
" ");
1007 m_recoNodes.emplace_back(pReco);
1008 m_sharedHits.emplace_back(nSharedHits);
1015 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1016 if (iter == m_recoNodes.end())
1017 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1018 int index = iter - m_recoNodes.begin();
1020 return static_cast<int>(m_sharedHits[index]);
1027 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1028 if (iter == m_recoNodes.end())
1029 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1031 const CaloHitList &recoHits{pReco->
GetCaloHits()};
1032 const CaloHitList &mcHits{m_pMCParticle->GetCaloHits()};
1033 CaloHitVector intersection;
1034 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1036 return this->GetPurity(intersection, recoHits, adcWeighted);
1044 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1045 if (iter == m_recoNodes.end())
1046 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1048 CaloHitList recoHits;
1049 for (
const CaloHit *pCaloHit : pReco->
GetCaloHits())
1050 if (pCaloHit->GetHitType() == view)
1051 recoHits.emplace_back(pCaloHit);
1053 for (
const CaloHit *pCaloHit : m_pMCParticle->GetCaloHits())
1054 if (pCaloHit->GetHitType() == view)
1055 mcHits.emplace_back(pCaloHit);
1057 CaloHitVector intersection;
1058 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1060 return this->GetPurity(intersection, recoHits, adcWeighted);
1067 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1068 if (iter == m_recoNodes.end())
1069 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1071 const CaloHitList &recoHits{pReco->
GetCaloHits()};
1072 const CaloHitList &mcHits{m_pMCParticle->GetCaloHits()};
1073 CaloHitVector intersection;
1074 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1076 return this->GetCompleteness(intersection, mcHits, adcWeighted);
1084 auto iter{std::find(m_recoNodes.begin(), m_recoNodes.end(), pReco)};
1085 if (iter == m_recoNodes.end())
1086 throw StatusCodeException(STATUS_CODE_NOT_FOUND);
1088 CaloHitList recoHits;
1089 for (
const CaloHit *pCaloHit : pReco->
GetCaloHits())
1090 if (pCaloHit->GetHitType() == view)
1091 recoHits.emplace_back(pCaloHit);
1093 for (
const CaloHit *pCaloHit : m_pMCParticle->GetCaloHits())
1094 if (pCaloHit->GetHitType() == view)
1095 mcHits.emplace_back(pCaloHit);
1097 CaloHitVector intersection;
1098 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1100 return this->GetCompleteness(intersection, mcHits, adcWeighted);
1108 if (!intersection.empty())
1113 for (
const CaloHit *pCaloHit : recoHits)
1114 adcSum += pCaloHit->GetInputEnergy();
1115 if (adcSum > std::numeric_limits<float>::epsilon())
1117 for (
const CaloHit *pCaloHit : intersection)
1118 purity += pCaloHit->GetInputEnergy();
1124 purity = intersection.size() /
static_cast<float>(recoHits.size());
1135 float completeness{0.f};
1136 if (!intersection.empty())
1141 for (
const CaloHit *pCaloHit : mcHits)
1142 adcSum += pCaloHit->GetInputEnergy();
1143 if (adcSum > std::numeric_limits<float>::epsilon())
1145 for (
const CaloHit *pCaloHit : intersection)
1146 completeness += pCaloHit->GetInputEnergy();
1147 completeness /= adcSum;
1152 completeness = intersection.size() /
static_cast<float>(mcHits.size());
1156 return completeness;
1163 if (m_recoNodes.size() != 1)
1166 if (this->GetPurity(m_recoNodes.front()) < qualityCuts.
m_minPurity)
1185 m_pMCNeutrino{
nullptr},
1186 m_pRecoNeutrino{
nullptr},
1187 m_qualityCuts{qualityCuts}
1202 std::sort(mcNodes.begin(), mcNodes.end(),
1204 std::sort(recoNodes.begin(), recoNodes.end(),
1207 std::map<const MCHierarchy::Node *, MCMatches> mcToMatchMap;
1210 const CaloHitList &recoHits{pRecoNode->GetCaloHits()};
1212 size_t bestSharedHits{0};
1215 if (!pMCNode->IsReconstructable())
1217 const CaloHitList &mcHits{pMCNode->
GetCaloHits()};
1218 CaloHitVector intersection;
1219 std::set_intersection(mcHits.begin(), mcHits.end(), recoHits.begin(), recoHits.end(), std::back_inserter(intersection));
1221 if (!intersection.empty())
1223 const size_t sharedHits{intersection.size()};
1224 if (sharedHits > bestSharedHits)
1226 bestSharedHits = sharedHits;
1227 pBestNode = pMCNode;
1233 auto iter{mcToMatchMap.find(pBestNode)};
1234 if (iter != mcToMatchMap.end())
1237 match.
AddRecoMatch(pRecoNode, static_cast<int>(bestSharedHits));
1242 match.
AddRecoMatch(pRecoNode, static_cast<int>(bestSharedHits));
1243 mcToMatchMap.insert(std::make_pair(pBestNode, match));
1248 m_unmatchedReco.emplace_back(pRecoNode);
1252 for (
auto [pMCNode, matches] : mcToMatchMap)
1253 m_matches.emplace_back(matches);
1256 return lhs.
GetMC()->
GetCaloHits().size() > rhs.GetMC()->GetCaloHits().size();
1258 std::sort(m_matches.begin(), m_matches.end(), predicate);
1262 if (pMCNode->IsReconstructable() && mcToMatchMap.find(pMCNode) == mcToMatchMap.end())
1265 m_matches.emplace_back(match);
1274 return static_cast<unsigned int>(m_matches.size());
1281 unsigned int nNodes{0};
1282 for (
const MCMatches &match : m_matches)
1285 if (!(pNode->IsCosmicRay() || pNode->IsTestBeamParticle()))
1296 unsigned int nNodes{0};
1297 for (
const MCMatches &match : m_matches)
1300 if (pNode->IsCosmicRay())
1311 unsigned int nNodes{0};
1312 for (
const MCMatches &match : m_matches)
1315 if (pNode->IsTestBeamParticle())
1326 unsigned int nNeutrinoMCParticles{this->GetNNeutrinoMCNodes()}, nNeutrinoRecoParticles{0};
1327 unsigned int nCosmicMCParticles{this->GetNCosmicRayMCNodes()}, nCosmicRecoParticles{0}, nCosmicRecoBTParticles{0};
1328 unsigned int nTestBeamMCParticles{this->GetNTestBeamMCNodes()}, nTestBeamRecoParticles{0}, nTestBeamRecoBTParticles{0};
1329 std::cout <<
"=== Matches ===" << std::endl;
1330 for (
const MCMatches &match : m_matches)
1333 const int pdg{pMCNode->GetParticleId()};
1334 const size_t mcHits{pMCNode->GetCaloHits().size()};
1335 const std::string tag{pMCNode->IsTestBeamParticle() ?
"(Beam) " : pMCNode->IsCosmicRay() ?
"(Cosmic) " :
""};
1336 std::cout <<
"MC " << tag <<
pdg <<
" hits " << mcHits << std::endl;
1341 const unsigned int recoHits{
static_cast<unsigned int>(pRecoNode->GetCaloHits().size())};
1342 const unsigned int sharedHits{match.GetSharedHits(pRecoNode)};
1343 const float purity{match.GetPurity(pRecoNode)};
1344 const float completeness{match.GetCompleteness(pRecoNode)};
1345 std::cout <<
" Matched " << sharedHits <<
" out of " << recoHits <<
" with purity " << purity <<
" and completeness "
1346 << completeness << std::endl;
1348 if (nodeVector.empty())
1351 if (pMCNode->IsTestBeamParticle())
1352 ++nTestBeamRecoParticles;
1353 else if (pMCNode->IsCosmicRay())
1354 ++nCosmicRecoParticles;
1356 ++nNeutrinoRecoParticles;
1361 std::cout <<
"Neutrino Interaction Summary:" << std::endl;
1362 std::cout << std::fixed << std::setprecision(1);
1363 if (nNeutrinoMCParticles)
1365 std::cout <<
"Matched final state particles: " << nNeutrinoRecoParticles <<
" of " << nNeutrinoMCParticles <<
" : "
1366 << (100 * nNeutrinoRecoParticles /
static_cast<float>(nNeutrinoMCParticles)) <<
"%" << std::endl;
1368 if (nCosmicMCParticles)
1370 std::cout <<
"Matched cosmics: " << nCosmicRecoParticles <<
" of " << nCosmicMCParticles <<
" : "
1371 << (100 * nCosmicRecoParticles /
static_cast<float>(nCosmicMCParticles)) <<
"%" << std::endl;
1376 std::cout <<
"Test Beam Interaction Summary:" << std::endl;
1377 std::cout << std::fixed << std::setprecision(1);
1378 if (nTestBeamMCParticles)
1380 std::cout <<
"Matched test beam particles: " << nTestBeamRecoParticles <<
" of " << nTestBeamMCParticles <<
" : "
1381 << (100 * nTestBeamRecoParticles /
static_cast<float>(nTestBeamMCParticles)) <<
"%" << std::endl;
1382 std::cout <<
"Loosely matched test beam particles: " << (nTestBeamRecoParticles + nTestBeamRecoBTParticles) <<
" of " << nTestBeamMCParticles
1383 <<
" : " << (100 * (nTestBeamRecoParticles + nTestBeamRecoBTParticles) /
static_cast<float>(nTestBeamMCParticles))
1384 <<
"%" << std::endl;
1386 if (nCosmicMCParticles)
1388 std::cout <<
"Matched cosmics: " << nCosmicRecoParticles <<
" of " << nCosmicMCParticles <<
" : "
1389 << (100 * nCosmicRecoParticles /
static_cast<float>(nCosmicMCParticles)) <<
"%" << std::endl;
1390 std::cout <<
"Loosely matched cosmics: " << (nCosmicRecoParticles + nCosmicRecoBTParticles) <<
" of " << nCosmicMCParticles <<
" : "
1391 << (100 * (nCosmicRecoParticles + nCosmicRecoBTParticles) /
static_cast<float>(nCosmicMCParticles)) <<
"%" << std::endl;
1394 if (!this->GetUnmatchedReco().empty())
1395 std::cout <<
"Unmatched reco: " << this->GetUnmatchedReco().size() << std::endl;
1404 hierarchy.
FillHierarchy(mcParticleList, caloHitList, foldParameters);
1418 matchInfo.
Match(mcHierarchy, recoHierarchy);
1426 const MCParticle *pRoot{
nullptr};
1427 for (
const MCParticle *pMCParticle : mcParticleList)
1433 primaries.insert(pPrimary);
1435 primaries.insert(pPrimary);
1437 catch (
const StatusCodeException &)
1440 pRoot = pMCParticle;
1441 else if (pMCParticle->GetParticleId() != 111 && pMCParticle->GetParticleId() < 1e9)
1442 std::cout <<
"LArHierarchyHelper::MCHierarchy::FillHierarchy: MC particle with PDG code " << pMCParticle->GetParticleId()
1443 <<
" at address " << pMCParticle <<
" has no associated primary particle" << std::endl;
1452 const ParticleFlowObject *pRoot{
nullptr};
1454 for (
const ParticleFlowObject *pPfo : pfoList)
1476 primaries.insert(pPfo);
1484 cosmicPfos.insert(pPfo);
1491 const PfoList &children{pRoot->GetDaughterPfoList()};
1492 for (
const ParticleFlowObject *pPrimary : children)
1493 primaries.insert(pPrimary);
1497 for (
const ParticleFlowObject *pPfo : cosmicPfos)
1498 primaries.insert(pPfo);
const pandora::MCParticle * m_pNeutrino
The incident neutrino, if it exists.
const pandora::CaloHitList & GetCaloHits() const
Retrieve the CaloHits associated with this node.
std::set< const pandora::MCParticle * > MCParticleSet
FoldingParameters()
Default constructor.
static bool SortByNHits(const pandora::ParticleFlowObject *const pLhs, const pandora::ParticleFlowObject *const pRhs)
Sort pfos by number of constituent hits.
bool m_foldToTier
Whether or not to apply folding based on particle tier.
int m_tier
If folding to a tier, the tier to be combined with its child particles.
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...
MatchInfo()
Default constructor.
const std::string ToString() const
Produce a string representation of the hierarchy.
std::vector< const Node * > NodeVector
const std::string ToString(const std::string &prefix) const
Produce a string representation of the hierarchy.
MCHierarchy()
Default constructor.
ReconstructabilityCriteria()
Default constructor.
static int GetHierarchyTier(const pandora::MCParticle *const pMCParticle)
Determine the position in the hierarchy for the MCParticle.
static const pandora::MCParticle * GetPrimaryMCParticle(const pandora::MCParticle *const pMCParticle)
Get the primary parent mc particle.
void GetFlattenedNodes(NodeVector &nodeVector) const
Retrieve a flat vector of the ndoes in the hierarchy.
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...
virtual ~RecoHierarchy()
Destructor.
const pandora::CaloHitList & GetCaloHits() const
Retrieve the CaloHits associated with this node.
static void MatchHierarchies(const MCHierarchy &mcHierarchy, const RecoHierarchy &recoHierarchy, MatchInfo &matchInfo)
Finds the matches between reconstructed and MC hierarchies.
static const pandora::ParticleFlowObject * GetRecoPrimaries(const pandora::PfoList &pfoList, PfoSet &primaries)
Retrieves the primary PFOs from a list and returns the root (neutrino) for hierarchy, if it exists.
Node(const RecoHierarchy &hierarchy, const pandora::ParticleFlowObject *pPfo)
Create a node with a primary PFO.
unsigned int GetNTestBeamMCNodes() const
Retrieve the number of test beam derived MC nodes available to match.
unsigned int GetNNeutrinoMCNodes() const
Retrieve the number of neutrino interaction derived MC nodes available to match.
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.
bool IsQuality(const QualityCuts &qualityCuts) const
Get whether this match passes quality cuts.
const MCHierarchy::Node * GetMC() const
Retrieve the MC node.
unsigned int GetNMCNodes() const
Retrieve the number of MC nodes available to match.
MCMatches(const MCHierarchy::Node *pMCParticle)
Constructor.
void Match(const MCHierarchy &mcHierarchy, const RecoHierarchy &recoHierarchy)
Match the nodes in the MC and reco hierarchies.
static bool SortByMomentum(const pandora::MCParticle *const pLhs, const pandora::MCParticle *const pRhs)
Sort mc particles by their momentum.
void FillHierarchy(const pandora::PfoList &pfoList, const FoldingParameters &foldParameters)
Creates a reconstructed hierarchy representation. Without folding this will be a mirror image of the ...
virtual ~Node()
Destructor.
static bool IsCosmicRay(const pandora::MCParticle *const pMCParticle)
Return true if passed a primary cosmic ray MCParticle.
static bool IsInelasticScatter(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle came from an inelastic scattering process.
bool IsNeutrinoHierarchy() const
Check if this is a neutrino hierarchy.
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...
const pandora::MCParticle * GetLeadingMCParticle() const
Retrieve the leading MC particle associated with this node.
std::vector< const Node * > NodeVector
static int GetHierarchyTier(const pandora::ParticleFlowObject *const pPfo)
Determine the position in the hierarchy for the MCParticle.
static bool IsTestBeam(const pandora::ParticleFlowObject *const pPfo)
Whether a pfo is a test beam particle.
static const pandora::ParticleFlowObject * GetParentPfo(const pandora::ParticleFlowObject *const pPfo)
Get the primary parent pfo.
const pandora::MCParticle * GetNeutrino() const
Retrieve the neutrino at the root of the hierarchy if it exists.
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...
const float m_minPurity
The minimum purity for a match to be considered good.
static bool IsBeamParticle(const pandora::MCParticle *const pMCParticle)
Returns true if passed a primary beam MCParticle.
bool IsReconstructable() const
Return whether or not this node should be considered reconstructable.
ReconstructabilityCriteria class.
bool IsCosmicRay() const
Check if this is a cosmic ray particle.
std::list< const Node * > NodeList
void FillHierarchy(const pandora::ParticleFlowObject *pRoot, const FoldingParameters &foldParameters)
Recursively fill the hierarchy based on the criteria established for this RecoHierarchy.
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.
static bool IsTriggeredBeamParticle(const pandora::MCParticle *const pMCParticle)
Returns true if passed a primary triggered beam MCParticle.
const std::string ToString(const std::string &prefix) const
Produce a string representation of the hierarchy.
float m_cosAngleTolerance
Cosine of the maximum angle at which topologies can be considered continuous.
virtual ~MCHierarchy()
Destructor.
static bool IsNeutrino(const pandora::ParticleFlowObject *const pPfo)
Whether a pfo is a neutrino or (antineutrino)
virtual ~Node()
Destructor.
bool m_foldDynamic
Whether or not to use process and topological information to make folding decisions.
const pandora::PfoList & GetRecoParticles() const
Retrieve the PFOs associated with this node.
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. If the parent does not travel any distance, a travelling parent is sought and the comparison made between this and the child. If no travelling parent can be found, the particles are treated as continuous.
void Print(const MCHierarchy &mcHierarchy) const
Prints information about which reco nodes are matched to the MC nodes, information about hit sharing...
bool m_foldToLeadingShowers
Whether or not to fold shower children to the leading shower particle.
const pandora::ParticleFlowObject * GetNeutrino() const
Retrieve the neutrino at the root of the hierarchy if it exists.
Header file for the lar hierarchy helper class.
QualityCuts()
Default constructor.
void GetFlattenedNodes(NodeVector &nodeVector) const
Retrieve a flat vector of the nodes in the hierarchy.
required by fuzzyCluster table::sbnd_g4_services gaushitTruthMatch pandora
const std::string ToString() const
Produce a string representation of the hierarchy.
std::string to_string(WindowPattern const &pattern)
bool IsReconstructable(const pandora::MCParticle *pMCParticle) const
Checks if an individual particle meets reconstructability criteria.
void SetLeadingLepton()
Tags the particle as the leading lepton.
Node(MCHierarchy &hierarchy, const pandora::MCParticle *pMCParticle, const int tier=1)
Create a node with a primary MC particle.
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 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::list< const Node * > NodeList
int GetParticleId() const
Retrieve the PDG code for the leading particle in this node Note, for reco objects the PDG codes repr...
bool IsTestBeamHierarchy() const
Check if this is a test beam hierarchy.
void FillFlat(const pandora::MCParticle *pRoot)
Fill this node by folding all descendent particles to this node.
int GetId() const
Retrieve the unique ID of this node.
const unsigned int m_minHits
the minimum number of primary good Hits
void RegisterNode(const Node *pNode)
Register a node with the hierarchy.
static void GetAllDescendentMCParticles(const pandora::MCParticle *const pMCParticle, pandora::MCParticleList &descendentMCParticleList)
Get all descendent mc particles.
float GetCompleteness(const RecoHierarchy::Node *pReco, const bool adcWeighted=false) const
Retrieve the completeness of the match.
RecoHierarchy()
Default constructor.
void FillHierarchy(const pandora::MCParticle *pRoot, const FoldingParameters &foldParameters)
Recursively fill the hierarchy based on the criteria established for this MCHierarchy.
bool IsTestBeamParticle() const
Check if this is a test beam particle.
std::set< const pandora::ParticleFlowObject * > PfoSet
const float m_minCompleteness
The minimum completeness for a match to be considered good.
static bool IsElasticScatter(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle came from an elastic scattering process.
static const pandora::MCParticle * GetMCPrimaries(const pandora::MCParticleList &mcParticleList, MCParticleSet &primaries)
Retrieves the primary MC particles from a list and returns the root (neutrino) for hierarchy...
BEGIN_PROLOG could also be cout
static bool IsNeutrino(const pandora::MCParticle *const pMCParticle)
Whether a mc particle is a neutrino or antineutrino.
void FillFlat(const pandora::ParticleFlowObject *pRoot)
Fill this node by folding all descendent particles to this node.
unsigned int GetNCosmicRayMCNodes() const
Retrieve the number of cosmic ray derived MC nodes available to match.
void AddRecoMatch(const RecoHierarchy::Node *pReco, const int nSharedHits)
Add a reconstructed node as a match for this MC node.