All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
LArMCParticleHelper.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArHelpers/LArMCParticleHelper.cc
3  *
4  * @brief Implementation of the lar monte carlo particle helper class.
5  *
6  * $Log: $
7  */
8 
9 #include "Helpers/MCParticleHelper.h"
10 
11 #include "Objects/CaloHit.h"
12 #include "Objects/Cluster.h"
13 #include "Objects/MCParticle.h"
14 
15 #include "Pandora/PdgTable.h"
16 #include "Pandora/StatusCodes.h"
17 
22 
23 #include <algorithm>
24 #include <cstdlib>
25 
26 namespace lar_content
27 {
28 
29 using namespace pandora;
30 
32  m_minPrimaryGoodHits(15),
33  m_minHitsForGoodView(5),
34  m_minPrimaryGoodViews(2),
35  m_selectInputHits(true),
36  m_maxPhotonPropagation(2.5f),
37  m_minHitSharingFraction(0.9f),
38  m_foldBackHierarchy(true)
39 {
40 }
41 
42 //------------------------------------------------------------------------------------------------------------------------------------------
43 //------------------------------------------------------------------------------------------------------------------------------------------
44 
45 bool LArMCParticleHelper::DoesPrimaryMeetCriteria(const MCParticle *const pMCParticle, std::function<bool(const MCParticle *const)> fCriteria)
46 {
47  try
48  {
49  const MCParticle *const pPrimaryMCParticle = LArMCParticleHelper::GetPrimaryMCParticle(pMCParticle);
50  return fCriteria(pPrimaryMCParticle);
51  }
52  catch (const StatusCodeException &)
53  {
54  }
55 
56  return false;
57 }
58 
59 //------------------------------------------------------------------------------------------------------------------------------------------
60 
61 bool LArMCParticleHelper::DoesLeadingMeetCriteria(const MCParticle *const pMCParticle, std::function<bool(const MCParticle *const)> fCriteria)
62 {
63  try
64  {
65  const MCParticle *const pLeadingMCParticle = LArMCParticleHelper::GetLeadingMCParticle(pMCParticle);
66  return fCriteria(pLeadingMCParticle);
67  }
68  catch (const StatusCodeException &)
69  {
70  }
71 
72  return false;
73 }
74 
75 //------------------------------------------------------------------------------------------------------------------------------------------
76 
77 bool LArMCParticleHelper::IsBeamNeutrinoFinalState(const MCParticle *const pMCParticle)
78 {
79  const MCParticle *const pParentMCParticle(LArMCParticleHelper::GetParentMCParticle(pMCParticle));
80  return (LArMCParticleHelper::IsPrimary(pMCParticle) && LArMCParticleHelper::IsNeutrino(pParentMCParticle));
81 }
82 
83 //------------------------------------------------------------------------------------------------------------------------------------------
84 
85 bool LArMCParticleHelper::IsTriggeredBeamParticle(const MCParticle *const pMCParticle)
86 {
87  const int nuance(LArMCParticleHelper::GetNuanceCode(pMCParticle));
88  return (LArMCParticleHelper::IsPrimary(pMCParticle) && (nuance == 2001));
89 }
90 
91 //------------------------------------------------------------------------------------------------------------------------------------------
92 
93 bool LArMCParticleHelper::IsBeamParticle(const MCParticle *const pMCParticle)
94 {
95  const int nuance(LArMCParticleHelper::GetNuanceCode(pMCParticle));
96  return (LArMCParticleHelper::IsPrimary(pMCParticle) && ((nuance == 2000) || (nuance == 2001)));
97 }
98 
99 //------------------------------------------------------------------------------------------------------------------------------------------
100 
101 bool LArMCParticleHelper::IsLeadingBeamParticle(const MCParticle *const pMCParticle)
102 {
103  // ATTN: Only the parent triggered beam particle has nuance code 2001
105  return (LArMCParticleHelper::IsLeading(pMCParticle) && (parentNuance == 2001 || parentNuance == 2000));
106 }
107 
108 //------------------------------------------------------------------------------------------------------------------------------------------
109 
110 bool LArMCParticleHelper::IsCosmicRay(const MCParticle *const pMCParticle)
111 {
112  const int nuance(LArMCParticleHelper::GetNuanceCode(pMCParticle));
113  return (LArMCParticleHelper::IsPrimary(pMCParticle) &&
114  ((nuance == 3000) || ((nuance == 0) && !LArMCParticleHelper::IsBeamNeutrinoFinalState(pMCParticle))));
115 }
116 
117 //------------------------------------------------------------------------------------------------------------------------------------------
118 
119 unsigned int LArMCParticleHelper::GetNuanceCode(const MCParticle *const pMCParticle)
120 {
121  const LArMCParticle *const pLArMCParticle(dynamic_cast<const LArMCParticle *>(pMCParticle));
122  if (pLArMCParticle)
123  return pLArMCParticle->GetNuanceCode();
124 
125  std::cout << "LArMCParticleHelper::GetNuanceCode - Error: Can't cast to LArMCParticle" << std::endl;
126  throw StatusCodeException(STATUS_CODE_NOT_ALLOWED);
127 }
128 
129 //------------------------------------------------------------------------------------------------------------------------------------------
130 
131 bool LArMCParticleHelper::IsNeutrino(const MCParticle *const pMCParticle)
132 {
133  const int nuance(LArMCParticleHelper::GetNuanceCode(pMCParticle));
134  if ((nuance == 0) || (nuance == 2000) || (nuance == 2001) || (nuance == 3000))
135  return false;
136 
137  const int absoluteParticleId(std::abs(pMCParticle->GetParticleId()));
138  return ((NU_E == absoluteParticleId) || (NU_MU == absoluteParticleId) || (NU_TAU == absoluteParticleId));
139 }
140 
141 //------------------------------------------------------------------------------------------------------------------------------------------
142 
143 bool LArMCParticleHelper::IsPrimary(const pandora::MCParticle *const pMCParticle)
144 {
145  try
146  {
147  return (LArMCParticleHelper::GetPrimaryMCParticle(pMCParticle) == pMCParticle);
148  }
149  catch (const StatusCodeException &)
150  {
151  }
152 
153  return false;
154 }
155 
156 //------------------------------------------------------------------------------------------------------------------------------------------
157 
158 bool LArMCParticleHelper::IsLeading(const pandora::MCParticle *const pMCParticle)
159 {
160  try
161  {
162  return (LArMCParticleHelper::GetLeadingMCParticle(pMCParticle) == pMCParticle);
163  }
164  catch (const StatusCodeException &)
165  {
166  }
167 
168  return false;
169 }
170 
171 //------------------------------------------------------------------------------------------------------------------------------------------
172 
173 int LArMCParticleHelper::GetHierarchyTier(const pandora::MCParticle *const pMCParticle)
174 {
175  const MCParticle *pParentMCParticle = pMCParticle;
176  int tier(0);
177 
178  while (pParentMCParticle->GetParentList().empty() == false)
179  {
180  if (1 != pParentMCParticle->GetParentList().size())
181  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
182 
183  pParentMCParticle = *(pParentMCParticle->GetParentList().begin());
184  ++tier;
185  }
186 
187  return tier;
188 }
189 
190 //------------------------------------------------------------------------------------------------------------------------------------------
191 
192 bool LArMCParticleHelper::IsVisible(const MCParticle *const pMCParticle)
193 {
194  const int absoluteParticleId(std::abs(pMCParticle->GetParticleId()));
195 
196  if ((E_MINUS == absoluteParticleId) || (MU_MINUS == absoluteParticleId) || (PI_PLUS == absoluteParticleId) || (K_PLUS == absoluteParticleId) ||
197  (SIGMA_MINUS == absoluteParticleId) || (SIGMA_PLUS == absoluteParticleId) || (HYPERON_MINUS == absoluteParticleId) ||
198  (PROTON == absoluteParticleId) || (PHOTON == absoluteParticleId) || (NEUTRON == absoluteParticleId))
199  return true;
200 
201  return false;
202 }
203 
204 //------------------------------------------------------------------------------------------------------------------------------------------
205 
206 void LArMCParticleHelper::GetTrueNeutrinos(const MCParticleList *const pMCParticleList, MCParticleVector &trueNeutrinos)
207 {
208  for (const MCParticle *const pMCParticle : *pMCParticleList)
209  {
210  if (LArMCParticleHelper::IsNeutrino(pMCParticle))
211  trueNeutrinos.push_back(pMCParticle);
212  }
213 
214  std::sort(trueNeutrinos.begin(), trueNeutrinos.end(), LArMCParticleHelper::SortByMomentum);
215 }
216 
217 //------------------------------------------------------------------------------------------------------------------------------------------
218 
219 void LArMCParticleHelper::GetTrueTestBeamParticles(const MCParticleList *const pMCParticleList, MCParticleVector &trueTestBeamParticles)
220 {
221  for (const MCParticle *const pMCParticle : *pMCParticleList)
222  {
224  trueTestBeamParticles.push_back(pMCParticle);
225  }
226 
227  std::sort(trueTestBeamParticles.begin(), trueTestBeamParticles.end(), LArMCParticleHelper::SortByMomentum);
228 }
229 
230 //------------------------------------------------------------------------------------------------------------------------------------------
231 
232 const MCParticle *LArMCParticleHelper::GetParentMCParticle(const MCParticle *const pMCParticle)
233 {
234  const MCParticle *pParentMCParticle = pMCParticle;
235 
236  while (pParentMCParticle->GetParentList().empty() == false)
237  {
238  if (1 != pParentMCParticle->GetParentList().size())
239  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
240 
241  pParentMCParticle = *(pParentMCParticle->GetParentList().begin());
242  }
243 
244  return pParentMCParticle;
245 }
246 
247 //------------------------------------------------------------------------------------------------------------------------------------------
248 
249 void LArMCParticleHelper::GetAllDescendentMCParticles(const pandora::MCParticle *const pMCParticle, pandora::MCParticleList &descendentMCParticleList)
250 {
251  for (const MCParticle *pDaughterMCParticle : pMCParticle->GetDaughterList())
252  {
253  if (std::find(descendentMCParticleList.begin(), descendentMCParticleList.end(), pDaughterMCParticle) == descendentMCParticleList.end())
254  {
255  descendentMCParticleList.emplace_back(pDaughterMCParticle);
256  LArMCParticleHelper::GetAllDescendentMCParticles(pDaughterMCParticle, descendentMCParticleList);
257  }
258  }
259 }
260 
261 //------------------------------------------------------------------------------------------------------------------------------------------
262 
263 void LArMCParticleHelper::GetAllDescendentMCParticles(const MCParticle *const pMCParticle, MCParticleList &descendentTrackParticles,
264  MCParticleList &leadingShowerParticles, MCParticleList &leadingNeutrons)
265 {
266  for (const MCParticle *pDaughterMCParticle : pMCParticle->GetDaughterList())
267  {
268  if (std::find(descendentTrackParticles.begin(), descendentTrackParticles.end(), pDaughterMCParticle) == descendentTrackParticles.end())
269  {
270  const int pdg{std::abs(pDaughterMCParticle->GetParticleId())};
271  if (pdg == E_MINUS || pdg == PHOTON)
272  {
273  leadingShowerParticles.emplace_back(pDaughterMCParticle);
274  }
275  else if (pdg == NEUTRON)
276  {
277  leadingNeutrons.emplace_back(pDaughterMCParticle);
278  }
279  else
280  {
281  descendentTrackParticles.emplace_back(pDaughterMCParticle);
282  LArMCParticleHelper::GetAllDescendentMCParticles(pDaughterMCParticle, descendentTrackParticles, leadingShowerParticles, leadingNeutrons);
283  }
284  }
285  }
286 }
287 
288 //------------------------------------------------------------------------------------------------------------------------------------------
289 
290 void LArMCParticleHelper::GetAllAncestorMCParticles(const pandora::MCParticle *const pMCParticle, pandora::MCParticleList &ancestorMCParticleList)
291 {
292  const MCParticleList &parentMCParticleList = pMCParticle->GetParentList();
293  if (parentMCParticleList.empty())
294  return;
295  if (parentMCParticleList.size() != 1)
296  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
297 
298  const MCParticle *pParentMCParticle = *parentMCParticleList.begin();
299  if (std::find(ancestorMCParticleList.begin(), ancestorMCParticleList.end(), pParentMCParticle) == ancestorMCParticleList.end())
300  {
301  ancestorMCParticleList.push_back(pParentMCParticle);
302  LArMCParticleHelper::GetAllAncestorMCParticles(pParentMCParticle, ancestorMCParticleList);
303  }
304 }
305 
306 //------------------------------------------------------------------------------------------------------------------------------------------
307 
308 void LArMCParticleHelper::GetPrimaryMCParticleList(const MCParticleList *const pMCParticleList, MCParticleVector &mcPrimaryVector)
309 {
310  for (const MCParticle *const pMCParticle : *pMCParticleList)
311  {
312  if (LArMCParticleHelper::IsPrimary(pMCParticle))
313  mcPrimaryVector.push_back(pMCParticle);
314  }
315 
316  std::sort(mcPrimaryVector.begin(), mcPrimaryVector.end(), LArMCParticleHelper::SortByMomentum);
317 }
318 
319 //------------------------------------------------------------------------------------------------------------------------------------------
320 
321 void LArMCParticleHelper::GetLeadingMCParticleList(const MCParticleList *const pMCParticleList, MCParticleVector &mcLeadingVector)
322 {
323  for (const MCParticle *const pMCParticle : *pMCParticleList)
324  {
325  const bool isBeamParticle(LArMCParticleHelper::IsBeamParticle(LArMCParticleHelper::GetParentMCParticle(pMCParticle)));
326 
327  if ((isBeamParticle && LArMCParticleHelper::IsLeading(pMCParticle)) || (!isBeamParticle && LArMCParticleHelper::IsPrimary(pMCParticle)))
328  {
329  mcLeadingVector.push_back(pMCParticle);
330  }
331  }
332 
333  std::sort(mcLeadingVector.begin(), mcLeadingVector.end(), LArMCParticleHelper::SortByMomentum);
334 }
335 
336 //------------------------------------------------------------------------------------------------------------------------------------------
337 
338 const MCParticle *LArMCParticleHelper::GetPrimaryMCParticle(const MCParticle *const pMCParticle)
339 {
340  // Navigate upward through MC daughter/parent links - collect this particle and all its parents
341  MCParticleVector mcVector;
342 
343  const MCParticle *pParentMCParticle = pMCParticle;
344  mcVector.push_back(pParentMCParticle);
345 
346  while (!pParentMCParticle->GetParentList().empty())
347  {
348  if (1 != pParentMCParticle->GetParentList().size())
349  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
350 
351  pParentMCParticle = *(pParentMCParticle->GetParentList().begin());
352  mcVector.push_back(pParentMCParticle);
353  }
354 
355  // Navigate downward through MC parent/daughter links - return the first long-lived charged particle
356  for (MCParticleVector::const_reverse_iterator iter = mcVector.rbegin(), iterEnd = mcVector.rend(); iter != iterEnd; ++iter)
357  {
358  const MCParticle *const pNextParticle = *iter;
359 
360  if (LArMCParticleHelper::IsVisible(pNextParticle))
361  return pNextParticle;
362  }
363 
364  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
365 }
366 
367 //------------------------------------------------------------------------------------------------------------------------------------------
368 
369 const MCParticle *LArMCParticleHelper::GetLeadingMCParticle(const MCParticle *const pMCParticle, const int hierarchyTierLimit)
370 {
371  // ATTN: If not beam particle return primary particle
372  const bool isBeamParticle(LArMCParticleHelper::IsBeamParticle(LArMCParticleHelper::GetParentMCParticle(pMCParticle)));
373 
374  if (!isBeamParticle)
375  return LArMCParticleHelper::GetPrimaryMCParticle(pMCParticle);
376 
377  // Navigate upward through MC daughter/parent links - collect this particle and all its parents
378  MCParticleVector mcVector;
379 
380  const MCParticle *pParentMCParticle = pMCParticle;
381  mcVector.push_back(pParentMCParticle);
382 
383  while (!pParentMCParticle->GetParentList().empty())
384  {
385  if (1 != pParentMCParticle->GetParentList().size())
386  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
387 
388  pParentMCParticle = *(pParentMCParticle->GetParentList().begin());
389  mcVector.push_back(pParentMCParticle);
390  }
391 
392  int hierarchyTier(0);
393  const MCParticle *pLeadingMCParticle(nullptr);
394 
395  // Navigate downward through MC parent/daughter links - return the first long-lived charged particle
396  for (MCParticleVector::const_reverse_iterator iter = mcVector.rbegin(), iterEnd = mcVector.rend(); iter != iterEnd; ++iter)
397  {
398  const MCParticle *const pNextParticle = *iter;
399 
400  // ATTN: Squash any invisible particles (e.g. pizero)
401  if (!LArMCParticleHelper::IsVisible(pNextParticle))
402  continue;
403 
404  if (hierarchyTier <= hierarchyTierLimit)
405  pLeadingMCParticle = pNextParticle;
406 
407  hierarchyTier++;
408  }
409 
410  if (!pLeadingMCParticle)
411  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
412 
413  return pLeadingMCParticle;
414 }
415 
416 //------------------------------------------------------------------------------------------------------------------------------------------
417 
418 void LArMCParticleHelper::GetMCPrimaryMap(const MCParticleList *const pMCParticleList, MCRelationMap &mcPrimaryMap)
419 {
420  for (const MCParticle *const pMCParticle : *pMCParticleList)
421  {
422  try
423  {
424  const MCParticle *const pPrimaryMCParticle = LArMCParticleHelper::GetPrimaryMCParticle(pMCParticle);
425  mcPrimaryMap[pMCParticle] = pPrimaryMCParticle;
426  }
427  catch (const StatusCodeException &)
428  {
429  }
430  }
431 }
432 
433 //------------------------------------------------------------------------------------------------------------------------------------------
434 
435 void LArMCParticleHelper::GetMCLeadingMap(const MCParticleList *const pMCParticleList, MCRelationMap &mcLeadingMap)
436 {
437  for (const MCParticle *const pMCParticle : *pMCParticleList)
438  {
439  try
440  {
441  const MCParticle *const pLeadingMCParticle = LArMCParticleHelper::GetLeadingMCParticle(pMCParticle);
442  mcLeadingMap[pMCParticle] = pLeadingMCParticle;
443  }
444  catch (const StatusCodeException &)
445  {
446  }
447  }
448 }
449 
450 //------------------------------------------------------------------------------------------------------------------------------------------
451 
452 void LArMCParticleHelper::GetMCToSelfMap(const MCParticleList *const pMCParticleList, MCRelationMap &mcToSelfMap)
453 {
454  for (const MCParticle *const pMCParticle : *pMCParticleList)
455  {
456  mcToSelfMap[pMCParticle] = pMCParticle;
457  }
458 }
459 
460 //------------------------------------------------------------------------------------------------------------------------------------------
461 
462 const MCParticle *LArMCParticleHelper::GetMainMCParticle(const ParticleFlowObject *const pPfo)
463 {
464  ClusterList clusterList;
465  LArPfoHelper::GetTwoDClusterList(pPfo, clusterList);
466  return MCParticleHelper::GetMainMCParticle(&clusterList);
467 }
468 
469 //------------------------------------------------------------------------------------------------------------------------------------------
470 
471 bool LArMCParticleHelper::SortByMomentum(const MCParticle *const pLhs, const MCParticle *const pRhs)
472 {
473  // Sort by momentum (prefer higher momentum)
474  const float momentumLhs(pLhs->GetMomentum().GetMagnitudeSquared());
475  const float momentumRhs(pRhs->GetMomentum().GetMagnitudeSquared());
476 
477  if (std::fabs(momentumLhs - momentumRhs) > std::numeric_limits<float>::epsilon())
478  return (momentumLhs > momentumRhs);
479 
480  // Sort by energy (prefer lighter particles)
481  if (std::fabs(pLhs->GetEnergy() - pRhs->GetEnergy()) > std::numeric_limits<float>::epsilon())
482  return (pLhs->GetEnergy() < pRhs->GetEnergy());
483 
484  // Sort by PDG code (prefer smaller numbers)
485  if (pLhs->GetParticleId() != pRhs->GetParticleId())
486  return (pLhs->GetParticleId() < pRhs->GetParticleId());
487 
488  // Sort by vertex position (tie-breaker)
489  const float positionLhs(pLhs->GetVertex().GetMagnitudeSquared());
490  const float positionRhs(pRhs->GetVertex().GetMagnitudeSquared());
491 
492  return (positionLhs < positionRhs);
493 }
494 
495 //------------------------------------------------------------------------------------------------------------------------------------------
496 
497 void LArMCParticleHelper::GetMCParticleToCaloHitMatches(const CaloHitList *const pCaloHitList, const MCRelationMap &mcToTargetMCMap,
498  CaloHitToMCMap &hitToMCMap, MCContributionMap &mcToTrueHitListMap)
499 {
500  for (const CaloHit *const pCaloHit : *pCaloHitList)
501  {
502  try
503  {
504  const MCParticle *const pHitParticle(MCParticleHelper::GetMainMCParticle(pCaloHit));
505  const MCParticle *pTargetParticle(pHitParticle);
506 
507  // ATTN Do not map back to target if mc to primary mc map or mc to self map not provided
508  if (!mcToTargetMCMap.empty())
509  {
510  MCRelationMap::const_iterator mcIter = mcToTargetMCMap.find(pHitParticle);
511 
512  if (mcToTargetMCMap.end() == mcIter)
513  continue;
514 
515  pTargetParticle = mcIter->second;
516  }
517 
518  mcToTrueHitListMap[pTargetParticle].push_back(pCaloHit);
519  hitToMCMap[pCaloHit] = pTargetParticle;
520  }
521  catch (StatusCodeException &statusCodeException)
522  {
523  if (STATUS_CODE_FAILURE == statusCodeException.GetStatusCode())
524  throw statusCodeException;
525  }
526  }
527 }
528 
529 //------------------------------------------------------------------------------------------------------------------------------------------
530 
531 void LArMCParticleHelper::SelectReconstructableMCParticles(const MCParticleList *pMCParticleList, const CaloHitList *pCaloHitList,
532  const PrimaryParameters &parameters, std::function<bool(const MCParticle *const)> fCriteria, MCContributionMap &selectedMCParticlesToHitsMap)
533 {
534  // Obtain map: [mc particle -> target mc particle]
535  LArMCParticleHelper::MCRelationMap mcToTargetMCMap;
536  parameters.m_foldBackHierarchy ? LArMCParticleHelper::GetMCPrimaryMap(pMCParticleList, mcToTargetMCMap)
537  : LArMCParticleHelper::GetMCToSelfMap(pMCParticleList, mcToTargetMCMap);
538 
539  // Remove non-reconstructable hits, e.g. those downstream of a neutron
540  // Unless selectInputHits == false
541  CaloHitList selectedCaloHitList;
542  LArMCParticleHelper::SelectCaloHits(pCaloHitList, mcToTargetMCMap, selectedCaloHitList, parameters.m_selectInputHits, parameters.m_maxPhotonPropagation);
543 
544  // Obtain maps: [hit -> target mc particle], [target mc particle -> list of hits]
545  CaloHitToMCMap trueHitToTargetMCMap;
546  MCContributionMap targetMCToTrueHitListMap;
547  LArMCParticleHelper::GetMCParticleToCaloHitMatches(&selectedCaloHitList, mcToTargetMCMap, trueHitToTargetMCMap, targetMCToTrueHitListMap);
548 
549  // Obtain vector: target mc particles
550  MCParticleVector targetMCVector;
551  if (parameters.m_foldBackHierarchy)
552  {
553  LArMCParticleHelper::GetPrimaryMCParticleList(pMCParticleList, targetMCVector);
554  }
555  else
556  {
557  std::copy(pMCParticleList->begin(), pMCParticleList->end(), std::back_inserter(targetMCVector));
558  }
559 
560  // Select MCParticles matching criteria
561  MCParticleVector candidateTargets;
562  LArMCParticleHelper::SelectParticlesMatchingCriteria(targetMCVector, fCriteria, candidateTargets, parameters, false);
563 
564  // Ensure the MCParticles have enough "good" hits to be reconstructed
565  LArMCParticleHelper::SelectParticlesByHitCount(candidateTargets, targetMCToTrueHitListMap, mcToTargetMCMap, parameters, selectedMCParticlesToHitsMap);
566 }
567 
568 //------------------------------------------------------------------------------------------------------------------------------------------
569 
570 void LArMCParticleHelper::SelectReconstructableTestBeamHierarchyMCParticles(const MCParticleList *pMCParticleList, const CaloHitList *pCaloHitList,
571  const PrimaryParameters &parameters, std::function<bool(const MCParticle *const)> fCriteria, MCContributionMap &selectedMCParticlesToHitsMap)
572 {
573  // Obtain map: [mc particle -> target mc particle]
574  LArMCParticleHelper::MCRelationMap mcToTargetMCMap;
575  parameters.m_foldBackHierarchy ? LArMCParticleHelper::GetMCLeadingMap(pMCParticleList, mcToTargetMCMap)
576  : LArMCParticleHelper::GetMCToSelfMap(pMCParticleList, mcToTargetMCMap);
577 
578  // Remove non-reconstructable hits, e.g. those downstream of a neutron
579  // Unless selectInputHits == false
580  CaloHitList selectedCaloHitList;
581  LArMCParticleHelper::SelectCaloHits(pCaloHitList, mcToTargetMCMap, selectedCaloHitList, parameters.m_selectInputHits, parameters.m_maxPhotonPropagation);
582 
583  // Obtain maps: [hit -> target mc particle], [target mc particle -> list of hits]
584  CaloHitToMCMap trueHitToTargetMCMap;
585  MCContributionMap targetMCToTrueHitListMap;
586  LArMCParticleHelper::GetMCParticleToCaloHitMatches(&selectedCaloHitList, mcToTargetMCMap, trueHitToTargetMCMap, targetMCToTrueHitListMap);
587 
588  // Obtain vector: target mc particles
589  MCParticleVector targetMCVector;
590  if (parameters.m_foldBackHierarchy)
591  {
592  LArMCParticleHelper::GetLeadingMCParticleList(pMCParticleList, targetMCVector);
593  }
594  else
595  {
596  std::copy(pMCParticleList->begin(), pMCParticleList->end(), std::back_inserter(targetMCVector));
597  }
598 
599  // Select MCParticles matching criteria
600  MCParticleVector candidateTargets;
601  LArMCParticleHelper::SelectParticlesMatchingCriteria(targetMCVector, fCriteria, candidateTargets, parameters, true);
602 
603  // Ensure the MCParticles have enough "good" hits to be reconstructed
604  LArMCParticleHelper::SelectParticlesByHitCount(candidateTargets, targetMCToTrueHitListMap, mcToTargetMCMap, parameters, selectedMCParticlesToHitsMap);
605 
606  // ATTN: The parent particle must be in the hierarchy map, event if not reconstructable
607  for (const MCParticle *const pMCParticle : candidateTargets)
608  {
609  if (!LArMCParticleHelper::IsBeamParticle(pMCParticle))
610  continue;
611 
612  const MCParticle *const pParentMCParticle(LArMCParticleHelper::GetParentMCParticle(pMCParticle));
613  if (selectedMCParticlesToHitsMap.find(pParentMCParticle) == selectedMCParticlesToHitsMap.end())
614  {
615  CaloHitList caloHitList;
616  selectedMCParticlesToHitsMap.insert(MCContributionMap::value_type(pParentMCParticle, caloHitList));
617  }
618  }
619 }
620 
621 //------------------------------------------------------------------------------------------------------------------------------------------
622 
623 void LArMCParticleHelper::GetPfoToReconstructable2DHitsMap(const PfoList &pfoList, const MCContributionMap &selectedMCParticleToHitsMap,
624  PfoContributionMap &pfoToReconstructable2DHitsMap, const bool foldBackHierarchy)
625 {
627  pfoList, MCContributionMapVector({selectedMCParticleToHitsMap}), pfoToReconstructable2DHitsMap, foldBackHierarchy);
628 }
629 
630 //------------------------------------------------------------------------------------------------------------------------------------------
631 
633  const MCContributionMap &selectedMCParticleToHitsMap, PfoContributionMap &pfoToReconstructable2DHitsMap, const bool foldBackHierarchy)
634 {
636  pfoList, MCContributionMapVector({selectedMCParticleToHitsMap}), pfoToReconstructable2DHitsMap, foldBackHierarchy);
637 }
638 
639 //------------------------------------------------------------------------------------------------------------------------------------------
640 
641 void LArMCParticleHelper::GetPfoToReconstructable2DHitsMap(const PfoList &pfoList, const MCContributionMapVector &selectedMCParticleToHitsMaps,
642  PfoContributionMap &pfoToReconstructable2DHitsMap, const bool foldBackHierarchy)
643 {
644  for (const ParticleFlowObject *const pPfo : pfoList)
645  {
646  CaloHitList pfoHitList;
647  LArMCParticleHelper::CollectReconstructable2DHits(pPfo, selectedMCParticleToHitsMaps, pfoHitList, foldBackHierarchy);
648 
649  if (!pfoToReconstructable2DHitsMap.insert(PfoContributionMap::value_type(pPfo, pfoHitList)).second)
650  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
651  }
652 }
653 
654 //------------------------------------------------------------------------------------------------------------------------------------------
655 
657  const MCContributionMapVector &selectedMCParticleToHitsMaps, PfoContributionMap &pfoToReconstructable2DHitsMap, const bool foldBackHierarchy)
658 {
659  for (const ParticleFlowObject *const pPfo : pfoList)
660  {
661  CaloHitList pfoHitList;
662  LArMCParticleHelper::CollectReconstructableTestBeamHierarchy2DHits(pPfo, selectedMCParticleToHitsMaps, pfoHitList, foldBackHierarchy);
663 
664  if (!pfoToReconstructable2DHitsMap.insert(PfoContributionMap::value_type(pPfo, pfoHitList)).second)
665  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
666  }
667 }
668 
669 //------------------------------------------------------------------------------------------------------------------------------------------
670 
672  const MCContributionMapVector &selectedMCParticleToHitsMaps, PfoToMCParticleHitSharingMap &pfoToMCParticleHitSharingMap,
673  MCParticleToPfoHitSharingMap &mcParticleToPfoHitSharingMap)
674 {
675  PfoVector sortedPfos;
676  for (const auto &mapEntry : pfoToReconstructable2DHitsMap)
677  sortedPfos.push_back(mapEntry.first);
678  std::sort(sortedPfos.begin(), sortedPfos.end(), LArPfoHelper::SortByNHits);
679 
680  for (const ParticleFlowObject *const pPfo : sortedPfos)
681  {
682  for (const MCContributionMap &mcParticleToHitsMap : selectedMCParticleToHitsMaps)
683  {
684  MCParticleVector sortedMCParticles;
685  for (const auto &mapEntry : mcParticleToHitsMap)
686  sortedMCParticles.push_back(mapEntry.first);
687  std::sort(sortedMCParticles.begin(), sortedMCParticles.end(), PointerLessThan<MCParticle>());
688 
689  for (const MCParticle *const pMCParticle : sortedMCParticles)
690  {
691  // Add map entries for this Pfo & MCParticle if required
692  if (pfoToMCParticleHitSharingMap.find(pPfo) == pfoToMCParticleHitSharingMap.end())
693  if (!pfoToMCParticleHitSharingMap.insert(PfoToMCParticleHitSharingMap::value_type(pPfo, MCParticleToSharedHitsVector())).second)
694  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT); // ATTN maybe overkill
695 
696  if (mcParticleToPfoHitSharingMap.find(pMCParticle) == mcParticleToPfoHitSharingMap.end())
697  if (!mcParticleToPfoHitSharingMap.insert(MCParticleToPfoHitSharingMap::value_type(pMCParticle, PfoToSharedHitsVector())).second)
698  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
699 
700  // Check this Pfo & MCParticle pairing hasn't already been checked
701  MCParticleToSharedHitsVector &mcHitPairs(pfoToMCParticleHitSharingMap.at(pPfo));
702  PfoToSharedHitsVector &pfoHitPairs(mcParticleToPfoHitSharingMap.at(pMCParticle));
703 
704  if (std::any_of(mcHitPairs.begin(), mcHitPairs.end(),
705  [&](const MCParticleCaloHitListPair &pair) { return (pair.first == pMCParticle); }))
706  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
707 
708  if (std::any_of(pfoHitPairs.begin(), pfoHitPairs.end(), [&](const PfoCaloHitListPair &pair) { return (pair.first == pPfo); }))
709  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
710 
711  // Add records to maps if there are any shared hits
712  const CaloHitList sharedHits(
713  LArMCParticleHelper::GetSharedHits(pfoToReconstructable2DHitsMap.at(pPfo), mcParticleToHitsMap.at(pMCParticle)));
714 
715  if (!sharedHits.empty())
716  {
717  mcHitPairs.push_back(MCParticleCaloHitListPair(pMCParticle, sharedHits));
718  pfoHitPairs.push_back(PfoCaloHitListPair(pPfo, sharedHits));
719 
720  std::sort(mcHitPairs.begin(), mcHitPairs.end(), [](const MCParticleCaloHitListPair &a, const MCParticleCaloHitListPair &b) -> bool {
721  return ((a.second.size() != b.second.size()) ? a.second.size() > b.second.size()
722  : LArMCParticleHelper::SortByMomentum(a.first, b.first));
723  });
724 
725  std::sort(pfoHitPairs.begin(), pfoHitPairs.end(), [](const PfoCaloHitListPair &a, const PfoCaloHitListPair &b) -> bool {
726  return ((a.second.size() != b.second.size()) ? a.second.size() > b.second.size() : LArPfoHelper::SortByNHits(a.first, b.first));
727  });
728  }
729  }
730  }
731  }
732 }
733 
734 //------------------------------------------------------------------------------------------------------------------------------------------
735 
736 void LArMCParticleHelper::GetClusterToReconstructable2DHitsMap(const pandora::ClusterList &clusterList,
737  const MCContributionMap &selectedMCToHitsMap, ClusterContributionMap &clusterToReconstructable2DHitsMap)
738 {
739  LArMCParticleHelper::GetClusterToReconstructable2DHitsMap(clusterList, MCContributionMapVector({selectedMCToHitsMap}), clusterToReconstructable2DHitsMap);
740 }
741 
742 //------------------------------------------------------------------------------------------------------------------------------------------
743 
744 void LArMCParticleHelper::GetClusterToReconstructable2DHitsMap(const pandora::ClusterList &clusterList,
745  const MCContributionMapVector &selectedMCToHitsMaps, ClusterContributionMap &clusterToReconstructable2DHitsMap)
746 {
747  for (const Cluster *const pCluster : clusterList)
748  {
749  CaloHitList caloHitList;
750  LArMCParticleHelper::CollectReconstructable2DHits(pCluster, selectedMCToHitsMaps, caloHitList);
751 
752  if (!clusterToReconstructable2DHitsMap.insert(ClusterContributionMap::value_type(pCluster, caloHitList)).second)
753  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
754  }
755 }
756 
757 //------------------------------------------------------------------------------------------------------------------------------------------
758 
759 bool LArMCParticleHelper::IsBremsstrahlung(const MCParticle *const pMCParticle)
760 {
761  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
762  if (!pLArMCParticle)
763  return false;
764 
765  switch (pLArMCParticle->GetProcess())
766  {
767  case MC_PROC_E_BREM:
768  case MC_PROC_MU_BREM:
769  case MC_PROC_HAD_BREM:
770  return true;
771  default:
772  return false;
773  }
774 
775  return false;
776 }
777 
778 //------------------------------------------------------------------------------------------------------------------------------------------
779 
780 bool LArMCParticleHelper::IsCapture(const MCParticle *const pMCParticle)
781 {
782  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
783  if (!pLArMCParticle)
784  return false;
785 
786  switch (pLArMCParticle->GetProcess())
787  {
789  case MC_PROC_N_CAPTURE:
793  return true;
794  default:
795  return false;
796  }
797 
798  return false;
799 }
800 
801 //------------------------------------------------------------------------------------------------------------------------------------------
802 
803 bool LArMCParticleHelper::IsDecay(const MCParticle *const pMCParticle)
804 {
805  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
806  if (!pLArMCParticle)
807  return false;
808 
809  switch (pLArMCParticle->GetProcess())
810  {
811  case MC_PROC_DECAY:
812  return true;
813  default:
814  return false;
815  }
816 
817  return false;
818 }
819 
820 //------------------------------------------------------------------------------------------------------------------------------------------
821 
822 bool LArMCParticleHelper::IsElasticScatter(const MCParticle *const pMCParticle)
823 {
824  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
825  if (!pLArMCParticle)
826  return false;
827 
828  switch (pLArMCParticle->GetProcess())
829  {
832  case MC_PROC_HAD_ELASTIC:
833  case MC_PROC_RAYLEIGH:
834  return true;
835  default:
836  return false;
837  }
838 
839  return false;
840 }
841 
842 //------------------------------------------------------------------------------------------------------------------------------------------
843 
844 bool LArMCParticleHelper::IsInelasticScatter(const MCParticle *const pMCParticle)
845 {
846  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
847  if (!pLArMCParticle)
848  return false;
849 
850  switch (pLArMCParticle->GetProcess())
851  {
852  case MC_PROC_COMPT:
871  return true;
872  default:
873  return false;
874  }
875 
876  return false;
877 }
878 
879 //------------------------------------------------------------------------------------------------------------------------------------------
880 
881 bool LArMCParticleHelper::IsIonisation(const MCParticle *const pMCParticle)
882 {
883  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
884  if (!pLArMCParticle)
885  return false;
886 
887  switch (pLArMCParticle->GetProcess())
888  {
889  case MC_PROC_E_IONI:
890  case MC_PROC_MU_IONI:
891  case MC_PROC_HAD_IONI:
892  case MC_PROC_ION_IONI:
893  return true;
894  default:
895  return false;
896  }
897 
898  return false;
899 }
900 
901 //------------------------------------------------------------------------------------------------------------------------------------------
902 
903 bool LArMCParticleHelper::IsNuclear(const MCParticle *const pMCParticle)
904 {
905  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
906  if (!pLArMCParticle)
907  return false;
908 
909  switch (pLArMCParticle->GetProcess())
910  {
913  case MC_PROC_MU_NUCLEAR:
914  return true;
915  default:
916  return false;
917  }
918 
919  return false;
920 }
921 
922 //------------------------------------------------------------------------------------------------------------------------------------------
923 
924 bool LArMCParticleHelper::IsPairProduction(const MCParticle *const pMCParticle)
925 {
926  const LArMCParticle *pLArMCParticle{dynamic_cast<const LArMCParticle *>(pMCParticle)};
927  if (!pLArMCParticle)
928  return false;
929 
930  switch (pLArMCParticle->GetProcess())
931  {
934  return true;
935  default:
936  return false;
937  }
938 
939  return false;
940 }
941 
942 //------------------------------------------------------------------------------------------------------------------------------------------
943 
944 CaloHitList LArMCParticleHelper::GetSharedHits(const CaloHitList &hitListA, const CaloHitList &hitListB)
945 {
946  CaloHitList sharedHits;
947 
948  for (const CaloHit *const pCaloHit : hitListA)
949  {
950  if (std::find(hitListB.begin(), hitListB.end(), pCaloHit) != hitListB.end())
951  sharedHits.push_back(pCaloHit);
952  }
953 
954  return sharedHits;
955 }
956 
957 //------------------------------------------------------------------------------------------------------------------------------------------
958 
959 bool LArMCParticleHelper::AreTopologicallyContinuous(const MCParticle *const pMCParent, const MCParticle *const pMCChild, const float cosAngleTolerance)
960 {
961  CartesianVector childDirection{pMCChild->GetEndpoint() - pMCChild->GetVertex()};
962  if (childDirection.GetMagnitude() < std::numeric_limits<float>::epsilon())
963  return true;
964  childDirection = childDirection.GetUnitVector();
965 
966  const MCParticle *pMCUpstream{pMCParent};
967  while (true)
968  {
969  CartesianVector parentDirection{pMCUpstream->GetEndpoint() - pMCUpstream->GetVertex()};
970  if (parentDirection.GetMagnitude() > std::numeric_limits<float>::epsilon())
971  {
972  parentDirection = parentDirection.GetUnitVector();
973  return parentDirection.GetDotProduct(childDirection) >= cosAngleTolerance;
974  }
975  else
976  {
977  const MCParticleList &parentList{pMCUpstream->GetParentList()};
978  const size_t size{parentList.size()};
979  if (size == 1)
980  pMCUpstream = parentList.front();
981  else if (size == 0)
982  return true;
983  else
984  return false;
985  }
986  }
987 
988  return false;
989 }
990 
991 // private
992 //------------------------------------------------------------------------------------------------------------------------------------------
993 
994 void LArMCParticleHelper::CollectReconstructable2DHits(const ParticleFlowObject *const pPfo,
995  const MCContributionMapVector &selectedMCParticleToHitsMaps, CaloHitList &reconstructableCaloHitList2D, const bool foldBackHierarchy)
996 {
997 
998  PfoList pfoList;
999 
1000  // If foldBackHierarchy collect all 2D calo hits in pfo hierarchy
1001  // else collect hits directly belonging to pfo
1002  if (foldBackHierarchy)
1003  {
1004  LArPfoHelper::GetAllDownstreamPfos(pPfo, pfoList);
1005  }
1006  else
1007  {
1008  pfoList.push_back(pPfo);
1009  }
1010 
1011  LArMCParticleHelper::CollectReconstructable2DHits(pfoList, selectedMCParticleToHitsMaps, reconstructableCaloHitList2D);
1012 }
1013 
1014 //------------------------------------------------------------------------------------------------------------------------------------------
1015 
1017  const MCContributionMapVector &selectedMCParticleToHitsMaps, CaloHitList &reconstructableCaloHitList2D, const bool foldBackHierarchy)
1018 {
1019 
1020  PfoList pfoList;
1021  // If foldBackHierarchy collect all 2D calo hits in pfo hierarchy
1022  // else collect hits directly belonging to pfo
1023  if (foldBackHierarchy)
1024  {
1025  // ATTN: Only collect downstream pfos for daughter test beam particles & cosmics
1026  if (pPfo->GetParentPfoList().empty() && LArPfoHelper::IsTestBeam(pPfo))
1027  {
1028  pfoList.push_back(pPfo);
1029  }
1030  else
1031  {
1032  LArPfoHelper::GetAllDownstreamPfos(pPfo, pfoList);
1033  }
1034  }
1035  else
1036  {
1037  pfoList.push_back(pPfo);
1038  }
1039 
1040  LArMCParticleHelper::CollectReconstructable2DHits(pfoList, selectedMCParticleToHitsMaps, reconstructableCaloHitList2D);
1041 }
1042 
1043 //------------------------------------------------------------------------------------------------------------------------------------------
1044 
1046  const PfoList &pfoList, const MCContributionMapVector &selectedMCParticleToHitsMaps, CaloHitList &reconstructableCaloHitList2D)
1047 {
1048  CaloHitList caloHitList2D;
1049  LArPfoHelper::GetCaloHits(pfoList, TPC_VIEW_U, caloHitList2D);
1050  LArPfoHelper::GetCaloHits(pfoList, TPC_VIEW_V, caloHitList2D);
1051  LArPfoHelper::GetCaloHits(pfoList, TPC_VIEW_W, caloHitList2D);
1052  LArPfoHelper::GetIsolatedCaloHits(pfoList, TPC_VIEW_U, caloHitList2D); // TODO check isolated usage throughout
1053  LArPfoHelper::GetIsolatedCaloHits(pfoList, TPC_VIEW_V, caloHitList2D);
1054  LArPfoHelper::GetIsolatedCaloHits(pfoList, TPC_VIEW_W, caloHitList2D);
1055 
1056  // Filter for only reconstructable hits
1057  for (const CaloHit *const pCaloHit : caloHitList2D)
1058  {
1059  bool isTargetHit(false);
1060  for (const MCContributionMap &mcParticleToHitsMap : selectedMCParticleToHitsMaps)
1061  {
1062  // ATTN This map is unordered, but this does not impact search for specific target hit
1063  for (const MCContributionMap::value_type &mapEntry : mcParticleToHitsMap)
1064  {
1065  if (std::find(mapEntry.second.begin(), mapEntry.second.end(), pCaloHit) != mapEntry.second.end())
1066  {
1067  isTargetHit = true;
1068  break;
1069  }
1070  }
1071  if (isTargetHit)
1072  break;
1073  }
1074 
1075  if (isTargetHit)
1076  reconstructableCaloHitList2D.push_back(pCaloHit);
1077  }
1078 }
1079 
1080 //------------------------------------------------------------------------------------------------------------------------------------------
1081 
1082 void LArMCParticleHelper::CollectReconstructable2DHits(const pandora::Cluster *const pCluster,
1083  const MCContributionMapVector &selectedMCToHitsMaps, pandora::CaloHitList &reconstructableCaloHitList2D)
1084 {
1085  const CaloHitList &isolatedCaloHitList{pCluster->GetIsolatedCaloHitList()};
1086  CaloHitList caloHitList;
1087  pCluster->GetOrderedCaloHitList().FillCaloHitList(caloHitList);
1088  for (const CaloHit *pCaloHit : isolatedCaloHitList)
1089  caloHitList.push_back(pCaloHit);
1090 
1091  // Filter for only reconstructable hits
1092  for (const CaloHit *const pCaloHit : caloHitList)
1093  {
1094  bool isTargetHit{false};
1095  for (const MCContributionMap &mcParticleToHitsMap : selectedMCToHitsMaps)
1096  { // ATTN This map is unordered, but this does not impact search for specific target hit
1097  for (const MCContributionMap::value_type &mapEntry : mcParticleToHitsMap)
1098  {
1099  if (std::find(mapEntry.second.begin(), mapEntry.second.end(), pCaloHit) != mapEntry.second.end())
1100  {
1101  isTargetHit = true;
1102  break;
1103  }
1104  }
1105  if (isTargetHit)
1106  break;
1107  }
1108 
1109  if (isTargetHit)
1110  reconstructableCaloHitList2D.push_back(pCaloHit);
1111  }
1112 }
1113 
1114 //------------------------------------------------------------------------------------------------------------------------------------------
1115 
1116 void LArMCParticleHelper::SelectCaloHits(const CaloHitList *const pCaloHitList, const LArMCParticleHelper::MCRelationMap &mcToTargetMCMap,
1117  CaloHitList &selectedCaloHitList, const bool selectInputHits, const float maxPhotonPropagation)
1118 {
1119  if (!selectInputHits)
1120  {
1121  selectedCaloHitList.insert(selectedCaloHitList.end(), pCaloHitList->begin(), pCaloHitList->end());
1122  return;
1123  }
1124 
1125  for (const CaloHit *const pCaloHit : *pCaloHitList)
1126  {
1127  try
1128  {
1129  const MCParticle *const pHitParticle(MCParticleHelper::GetMainMCParticle(pCaloHit));
1130 
1131  LArMCParticleHelper::MCRelationMap::const_iterator mcIter = mcToTargetMCMap.find(pHitParticle);
1132 
1133  if (mcToTargetMCMap.end() == mcIter)
1134  continue;
1135 
1136  // ATTN With folding on or off, still require primary particle to review hierarchy details
1137  const MCParticle *const pPrimaryParticle = LArMCParticleHelper::GetPrimaryMCParticle(pHitParticle);
1138 
1139  if (PassMCParticleChecks(pPrimaryParticle, pPrimaryParticle, pHitParticle, maxPhotonPropagation))
1140  selectedCaloHitList.push_back(pCaloHit);
1141  }
1142  catch (const StatusCodeException &)
1143  {
1144  }
1145  }
1146 }
1147 
1148 //------------------------------------------------------------------------------------------------------------------------------------------
1149 
1150 bool LArMCParticleHelper::IsDescendentOf(const MCParticle *const pMCParticle, const int pdg, const bool isChargeSensitive)
1151 {
1152  const MCParticle *pCurrentParticle = pMCParticle;
1153  while (!pCurrentParticle->GetParentList().empty())
1154  {
1155  if (pCurrentParticle->GetParentList().size() > 1)
1156  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
1157 
1158  const MCParticle *pParent = *(pCurrentParticle->GetParentList().begin());
1159  const bool found{isChargeSensitive ? pParent->GetParticleId() == pdg : std::abs(pParent->GetParticleId()) == std::abs(pdg)};
1160  if (found)
1161  return true;
1162  pCurrentParticle = pParent;
1163  }
1164 
1165  return false;
1166 }
1167 
1168 //------------------------------------------------------------------------------------------------------------------------------------------
1169 
1170 void LArMCParticleHelper::GetBreadthFirstHierarchyRepresentation(const MCParticle *const pMCParticle, MCParticleList &mcParticleList)
1171 {
1172  const MCParticle *const pRoot{LArMCParticleHelper::GetParentMCParticle(pMCParticle)};
1173  MCParticleList queue;
1174  mcParticleList.emplace_back(pRoot);
1175  queue.emplace_back(pRoot);
1176 
1177  while (!queue.empty())
1178  {
1179  const MCParticleList &daughters{queue.front()->GetDaughterList()};
1180  queue.pop_front();
1181  for (const MCParticle *pDaughter : daughters)
1182  {
1183  mcParticleList.emplace_back(pDaughter);
1184  queue.emplace_back(pDaughter);
1185  }
1186  }
1187 }
1188 
1189 //------------------------------------------------------------------------------------------------------------------------------------------
1190 
1191 void LArMCParticleHelper::SelectParticlesByHitCount(const MCParticleVector &candidateTargets, const MCContributionMap &mcToTrueHitListMap,
1192  const MCRelationMap &mcToTargetMCMap, const PrimaryParameters &parameters, MCContributionMap &selectedMCParticlesToHitsMap)
1193 {
1194  // Apply restrictions on the number of good hits associated with the MCParticles
1195  for (const MCParticle *const pMCTarget : candidateTargets)
1196  {
1197  MCContributionMap::const_iterator trueHitsIter = mcToTrueHitListMap.find(pMCTarget);
1198  if (mcToTrueHitListMap.end() == trueHitsIter)
1199  continue;
1200 
1201  const CaloHitList &caloHitList(trueHitsIter->second);
1202 
1203  // Remove shared hits where target particle deposits below threshold energy fraction
1204  CaloHitList goodCaloHitList;
1206  &caloHitList, mcToTargetMCMap, goodCaloHitList, parameters.m_selectInputHits, parameters.m_minHitSharingFraction);
1207 
1208  if (goodCaloHitList.size() < parameters.m_minPrimaryGoodHits)
1209  continue;
1210 
1211  unsigned int nGoodViews(0);
1212  if (LArMonitoringHelper::CountHitsByType(TPC_VIEW_U, goodCaloHitList) >= parameters.m_minHitsForGoodView)
1213  ++nGoodViews;
1214 
1215  if (LArMonitoringHelper::CountHitsByType(TPC_VIEW_V, goodCaloHitList) >= parameters.m_minHitsForGoodView)
1216  ++nGoodViews;
1217 
1218  if (LArMonitoringHelper::CountHitsByType(TPC_VIEW_W, goodCaloHitList) >= parameters.m_minHitsForGoodView)
1219  ++nGoodViews;
1220 
1221  if (nGoodViews < parameters.m_minPrimaryGoodViews)
1222  continue;
1223 
1224  if (!selectedMCParticlesToHitsMap.insert(MCContributionMap::value_type(pMCTarget, caloHitList)).second)
1225  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
1226  }
1227 }
1228 
1229 //------------------------------------------------------------------------------------------------------------------------------------------
1230 
1231 void LArMCParticleHelper::SelectGoodCaloHits(const CaloHitList *const pSelectedCaloHitList, const LArMCParticleHelper::MCRelationMap &mcToTargetMCMap,
1232  CaloHitList &selectedGoodCaloHitList, const bool selectInputHits, const float minHitSharingFraction)
1233 {
1234  if (!selectInputHits)
1235  {
1236  selectedGoodCaloHitList.insert(selectedGoodCaloHitList.end(), pSelectedCaloHitList->begin(), pSelectedCaloHitList->end());
1237  return;
1238  }
1239 
1240  for (const CaloHit *const pCaloHit : *pSelectedCaloHitList)
1241  {
1242  MCParticleVector mcParticleVector;
1243  for (const auto &mapEntry : pCaloHit->GetMCParticleWeightMap())
1244  mcParticleVector.push_back(mapEntry.first);
1245  std::sort(mcParticleVector.begin(), mcParticleVector.end(), PointerLessThan<MCParticle>());
1246 
1247  MCParticleWeightMap targetWeightMap;
1248 
1249  for (const MCParticle *const pMCParticle : mcParticleVector)
1250  {
1251  const float weight(pCaloHit->GetMCParticleWeightMap().at(pMCParticle));
1252  LArMCParticleHelper::MCRelationMap::const_iterator mcIter = mcToTargetMCMap.find(pMCParticle);
1253 
1254  if (mcToTargetMCMap.end() != mcIter)
1255  targetWeightMap[mcIter->second] += weight;
1256  }
1257 
1258  MCParticleVector mcTargetVector;
1259  for (const auto &mapEntry : targetWeightMap)
1260  mcTargetVector.push_back(mapEntry.first);
1261  std::sort(mcTargetVector.begin(), mcTargetVector.end(), PointerLessThan<MCParticle>());
1262 
1263  const MCParticle *pBestTargetParticle(nullptr);
1264  float bestTargetWeight(0.f), targetWeightSum(0.f);
1265 
1266  for (const MCParticle *const pTargetMCParticle : mcTargetVector)
1267  {
1268  const float targetWeight(targetWeightMap.at(pTargetMCParticle));
1269  targetWeightSum += targetWeight;
1270 
1271  if (targetWeight > bestTargetWeight)
1272  {
1273  bestTargetWeight = targetWeight;
1274  pBestTargetParticle = pTargetMCParticle;
1275  }
1276  }
1277 
1278  if (!pBestTargetParticle || (targetWeightSum < std::numeric_limits<float>::epsilon()) || ((bestTargetWeight / targetWeightSum) < minHitSharingFraction))
1279  continue;
1280 
1281  selectedGoodCaloHitList.push_back(pCaloHit);
1282  }
1283 }
1284 
1285 //------------------------------------------------------------------------------------------------------------------------------------------
1286 
1288  std::function<bool(const MCParticle *const)> fCriteria, MCParticleVector &selectedParticles, const PrimaryParameters &parameters, const bool isTestBeam)
1289 {
1290  for (const MCParticle *const pMCParticle : inputMCParticles)
1291  {
1292  if (parameters.m_foldBackHierarchy)
1293  {
1294  if (!fCriteria(pMCParticle))
1295  continue;
1296  }
1297  else
1298  {
1299  if (isTestBeam)
1300  {
1301  if (!LArMCParticleHelper::DoesLeadingMeetCriteria(pMCParticle, fCriteria))
1302  continue;
1303  }
1304  else
1305  {
1306  if (!LArMCParticleHelper::DoesPrimaryMeetCriteria(pMCParticle, fCriteria))
1307  continue;
1308  }
1309  }
1310  selectedParticles.push_back(pMCParticle);
1311  }
1312 }
1313 
1314 //------------------------------------------------------------------------------------------------------------------------------------------
1315 
1316 bool LArMCParticleHelper::PassMCParticleChecks(const MCParticle *const pOriginalPrimary, const MCParticle *const pThisMCParticle,
1317  const MCParticle *const pHitMCParticle, const float maxPhotonPropagation)
1318 {
1319  if (NEUTRON == std::abs(pThisMCParticle->GetParticleId()))
1320  return false;
1321 
1322  if ((PHOTON == pThisMCParticle->GetParticleId()) && (PHOTON != pOriginalPrimary->GetParticleId()) &&
1323  (E_MINUS != std::abs(pOriginalPrimary->GetParticleId())))
1324  {
1325  if ((pThisMCParticle->GetEndpoint() - pThisMCParticle->GetVertex()).GetMagnitude() > maxPhotonPropagation)
1326  return false;
1327  }
1328 
1329  if (pThisMCParticle == pHitMCParticle)
1330  return true;
1331 
1332  for (const MCParticle *const pDaughterMCParticle : pThisMCParticle->GetDaughterList())
1333  {
1334  if (PassMCParticleChecks(pOriginalPrimary, pDaughterMCParticle, pHitMCParticle, maxPhotonPropagation))
1335  return true;
1336  }
1337 
1338  return false;
1339 }
1340 
1341 } // namespace lar_content
static bool IsVisible(const pandora::MCParticle *const pMCParticle)
Whether a mc particle is visible (i.e. long-lived charged particle)
static bool PassMCParticleChecks(const pandora::MCParticle *const pOriginalPrimary, const pandora::MCParticle *const pThisMCParticle, const pandora::MCParticle *const pHitMCParticle, const float maxPhotonPropagation)
Whether it is possible to navigate from a primary mc particle to a downstream mc particle without &quot;pa...
unsigned int m_minPrimaryGoodViews
the minimum number of primary good views
static bool SortByNHits(const pandora::ParticleFlowObject *const pLhs, const pandora::ParticleFlowObject *const pRhs)
Sort pfos by number of constituent hits.
static bool IsNuclear(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle comes from a nuclear interaction process.
Header file for the pfo helper class.
std::unordered_map< const pandora::MCParticle *, pandora::CaloHitList > MCContributionMap
bool m_selectInputHits
whether to select input hits
var pdg
Definition: selectors.fcl:14
std::pair< const pandora::MCParticle *, pandora::CaloHitList > MCParticleCaloHitListPair
static void GetTestBeamHierarchyPfoToReconstructable2DHitsMap(const pandora::PfoList &pfoList, const MCContributionMap &selectedMCParticleToHitsMap, PfoContributionMap &pfoToReconstructable2DHitsMap, const bool foldBackHierarchy)
Get mapping from Pfo in reconstructed test beam hierarchy to reconstructable 2D hits (=good hits belo...
static bool IsPrimary(const pandora::MCParticle *const pMCParticle)
Whether a provided mc particle matches the implemented definition of being primary.
static void GetTwoDClusterList(const pandora::ParticleFlowObject *const pPfo, pandora::ClusterList &clusterList)
Get the list of 2D clusters from an input pfo.
static void GetLeadingMCParticleList(const pandora::MCParticleList *const pMCParticleList, pandora::MCParticleVector &mcLeadingVector)
Get vector of leading MC particles from an input list of MC particles.
static bool IsLeadingBeamParticle(const pandora::MCParticle *const pMCParticle)
Returns true if passed a leading beam MCParticle.
unsigned int m_minPrimaryGoodHits
the minimum number of primary good Hits
int GetNuanceCode() const
Get the nuance code.
static void GetPfoToReconstructable2DHitsMap(const pandora::PfoList &pfoList, const MCContributionMap &selectedMCParticleToHitsMap, PfoContributionMap &pfoToReconstructable2DHitsMap, const bool foldBackHierarchy)
Get mapping from Pfo to reconstructable 2D hits (=good hits belonging to a selected reconstructable M...
std::unordered_map< const pandora::CaloHit *, const pandora::MCParticle * > CaloHitToMCMap
static int GetHierarchyTier(const pandora::MCParticle *const pMCParticle)
Determine the position in the hierarchy for the MCParticle.
static void GetPfoMCParticleHitSharingMaps(const PfoContributionMap &pfoToReconstructable2DHitsMap, const MCContributionMapVector &selectedMCParticleToHitsMaps, PfoToMCParticleHitSharingMap &pfoToMCParticleHitSharingMap, MCParticleToPfoHitSharingMap &mcParticleToPfoHitSharingMap)
Get the mappings from Pfo -&gt; pair (reconstructable MCparticles, number of reconstructable 2D hits sha...
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
static const pandora::MCParticle * GetPrimaryMCParticle(const pandora::MCParticle *const pMCParticle)
Get the primary parent mc particle.
std::map< const pandora::MCParticle *, PfoToSharedHitsVector > MCParticleToPfoHitSharingMap
std::vector< PfoCaloHitListPair > PfoToSharedHitsVector
static void SelectGoodCaloHits(const pandora::CaloHitList *const pSelectedCaloHitList, const MCRelationMap &mcToTargetMCMap, pandora::CaloHitList &selectedGoodCaloHitList, const bool selectInputHits, const float minHitSharingFraction)
Apply further selection criteria to end up with a collection of &quot;good&quot; calo hits that can be use to d...
bool m_foldBackHierarchy
whether to fold the hierarchy back to the primary (neutrino) or leading particles (test beam) ...
static void SelectReconstructableTestBeamHierarchyMCParticles(const pandora::MCParticleList *pMCParticleList, const pandora::CaloHitList *pCaloHitList, const PrimaryParameters &parameters, std::function< bool(const pandora::MCParticle *const)> fCriteria, MCContributionMap &selectedMCParticlesToHitsMap)
Select target, reconstructable mc particles in the relevant hierarchy that match given criteria...
static void GetMCPrimaryMap(const pandora::MCParticleList *const pMCParticleList, MCRelationMap &mcPrimaryMap)
Get mapping from individual mc particles (in a provided list) and their primary parent mc particles...
static bool IsPairProduction(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle comes from a pair production process.
Header file for the lar monitoring helper helper class.
static const pandora::MCParticle * GetMainMCParticle(const pandora::ParticleFlowObject *const pPfo)
Find the mc particle making the largest contribution to 2D clusters in a specified pfo...
static unsigned int GetNuanceCode(const pandora::MCParticle *const pMCParticle)
Get the nuance code of an MCParticle.
unsigned int m_minHitsForGoodView
the minimum number of Hits for a good view
static const pandora::MCParticle * GetLeadingMCParticle(const pandora::MCParticle *const pMCParticle, const int hierarchyTierLimit=1)
Get the leading particle in the hierarchy, for use at ProtoDUNE.
float m_maxPhotonPropagation
the maximum photon propagation length
process_name gaushit a
static void SelectParticlesByHitCount(const pandora::MCParticleVector &candidateTargets, const MCContributionMap &mcToTrueHitListMap, const MCRelationMap &mcToTargetMCMap, const PrimaryParameters &parameters, MCContributionMap &selectedMCParticlesToHitsMap)
Filter an input vector of MCParticles to ensure they have sufficient good hits to be reconstructable...
static void CollectReconstructableTestBeamHierarchy2DHits(const pandora::ParticleFlowObject *const pPfo, const MCContributionMapVector &selectedMCParticleToHitsMaps, pandora::CaloHitList &reconstructableCaloHitList2D, const bool foldBackHierarchy)
For a given Pfo, collect the hits which are reconstructable (=good hits belonging to a selected recon...
static bool SortByMomentum(const pandora::MCParticle *const pLhs, const pandora::MCParticle *const pRhs)
Sort mc particles by their momentum.
T abs(T value)
LAr mc particle class.
Definition: LArMCParticle.h:94
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.
static bool IsLeading(const pandora::MCParticle *const pMCParticle)
Whether a provided mc particle matches the implemented definition of being leading.
static void SelectReconstructableMCParticles(const pandora::MCParticleList *pMCParticleList, const pandora::CaloHitList *pCaloHitList, const PrimaryParameters &parameters, std::function< bool(const pandora::MCParticle *const)> fCriteria, MCContributionMap &selectedMCParticlesToHitsMap)
Select target, reconstructable mc particles that match given criteria.
static bool IsTestBeam(const pandora::ParticleFlowObject *const pPfo)
Whether a pfo is a test beam particle.
static void GetMCToSelfMap(const pandora::MCParticleList *const pMCParticleList, MCRelationMap &mcToSelfMap)
Get mapping from individual mc particles (in a provided list) to themselves (to be used when not fold...
Header file for the lar monte carlo particle helper helper class.
Header file for the cluster helper class.
static void GetMCParticleToCaloHitMatches(const pandora::CaloHitList *const pCaloHitList, const MCRelationMap &mcToTargetMCMap, CaloHitToMCMap &hitToMCMap, MCContributionMap &mcToTrueHitListMap)
Match calo hits to their parent particles.
static bool IsBeamParticle(const pandora::MCParticle *const pMCParticle)
Returns true if passed a primary beam MCParticle.
std::unordered_map< const pandora::Cluster *, pandora::CaloHitList > ClusterContributionMap
std::vector< art::Ptr< simb::MCParticle > > MCParticleVector
static const pandora::MCParticle * GetParentMCParticle(const pandora::MCParticle *const pMCParticle)
Get the parent mc particle.
static bool IsTriggeredBeamParticle(const pandora::MCParticle *const pMCParticle)
Returns true if passed a primary triggered beam MCParticle.
float m_minHitSharingFraction
the minimum Hit sharing fraction
static void GetMCLeadingMap(const pandora::MCParticleList *const pMCParticleList, MCRelationMap &mcLeadingMap)
Get mapping from individual mc particles (in a provided list) and their leading parent mc particles...
static bool IsDecay(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle comes from a decay process.
std::vector< MCContributionMap > MCContributionMapVector
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.
static bool IsBremsstrahlung(const pandora::MCParticle *const pMCParticle)
static void GetPrimaryMCParticleList(const pandora::MCParticleList *const pMCParticleList, pandora::MCParticleVector &mcPrimaryVector)
Get vector of primary MC particles from an input list of MC particles.
static bool DoesPrimaryMeetCriteria(const pandora::MCParticle *const pMCParticle, std::function< bool(const pandora::MCParticle *const)> fCriteria)
Returns true if passed particle whose primary meets the passed criteria.
static void GetAllAncestorMCParticles(const pandora::MCParticle *const pMCParticle, pandora::MCParticleList &ancestorMCParticleList)
Get all ancestor mc particles.
required by fuzzyCluster table::sbnd_g4_services gaushitTruthMatch pandora
Definition: reco_sbnd.fcl:182
static void CollectReconstructable2DHits(const pandora::ParticleFlowObject *const pPfo, const MCContributionMapVector &selectedMCParticleToHitsMaps, pandora::CaloHitList &reconstructableCaloHitList2D, const bool foldBackHierarchy)
For a given Pfo, collect the hits which are reconstructable (=good hits belonging to a selected recon...
static void GetTrueNeutrinos(const pandora::MCParticleList *const pMCParticleList, pandora::MCParticleVector &trueNeutrinos)
Get neutrino MC particles from an input MC particle list.
std::pair< const pandora::ParticleFlowObject *, pandora::CaloHitList > PfoCaloHitListPair
static bool IsCapture(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle comes from a capture process.
static void GetIsolatedCaloHits(const pandora::PfoList &pfoList, const pandora::HitType &hitType, pandora::CaloHitList &caloHitList)
Get a list of isolated calo hits of a particular hit type from a list of pfos.
static void GetClusterToReconstructable2DHitsMap(const pandora::ClusterList &clusterList, const MCContributionMap &selectedMCToHitsMap, ClusterContributionMap &clusterToReconstructable2DHitsMap)
Get mapping from cluster to reconstructable 2D hits (=good hits belonging to a selected reconstructab...
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 bool IsIonisation(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle comes from an ionisation process.
static bool DoesLeadingMeetCriteria(const pandora::MCParticle *const pMCParticle, std::function< bool(const pandora::MCParticle *const)> fCriteria)
Returns true if passed particle whose leading meets the passed criteria.
T copy(T const &v)
std::vector< MCParticleCaloHitListPair > MCParticleToSharedHitsVector
static void SelectParticlesMatchingCriteria(const pandora::MCParticleVector &inputMCParticles, std::function< bool(const pandora::MCParticle *const)> fCriteria, pandora::MCParticleVector &selectedParticles, const PrimaryParameters &parameters, const bool isTestBeam)
Select mc particles matching given criteria from an input list.
static void GetTrueTestBeamParticles(const pandora::MCParticleList *const pMCParticleList, pandora::MCParticleVector &trueTestBeamParticles)
Get triggered test beam MC particles from an input MC particle list.
static bool IsDescendentOf(const pandora::MCParticle *const pMCParticle, const int pdg, const bool isChargeSensitive=false)
Determine if the MC particle is a descendent of a particle with the given PDG code.
std::unordered_map< const pandora::ParticleFlowObject *, pandora::CaloHitList > PfoContributionMap
static void GetAllDescendentMCParticles(const pandora::MCParticle *const pMCParticle, pandora::MCParticleList &descendentMCParticleList)
Get all descendent mc particles.
static void GetCaloHits(const pandora::PfoList &pfoList, const pandora::HitType &hitType, pandora::CaloHitList &caloHitList)
Get a list of calo hits of a particular hit type from a list of pfos.
static pandora::CaloHitList GetSharedHits(const pandora::CaloHitList &hitListA, const pandora::CaloHitList &hitListB)
Get the hits in the intersection of two hit lists.
static bool IsBeamNeutrinoFinalState(const pandora::MCParticle *const pMCParticle)
Returns true if passed a primary neutrino final state MCParticle.
static unsigned int CountHitsByType(const pandora::HitType hitType, const pandora::CaloHitList &caloHitList)
Count the number of calo hits, in a provided list, of a specified type.
std::unordered_map< const pandora::MCParticle *, const pandora::MCParticle * > MCRelationMap
static void GetBreadthFirstHierarchyRepresentation(const pandora::MCParticle *const pMCParticle, pandora::MCParticleList &mcParticleList)
Retrieve a linearised representation of the MC particle hierarchy in breadth first order...
static bool IsElasticScatter(const pandora::MCParticle *const pMCParticle)
Check whether or not an MC particle came from an elastic scattering process.
BEGIN_PROLOG don t mess with this pandoraTrackGausCryoW true
BEGIN_PROLOG could also be cout
std::map< const pandora::ParticleFlowObject *, MCParticleToSharedHitsVector > PfoToMCParticleHitSharingMap
static bool IsNeutrino(const pandora::MCParticle *const pMCParticle)
Whether a mc particle is a neutrino or antineutrino.
static void SelectCaloHits(const pandora::CaloHitList *const pCaloHitList, const MCRelationMap &mcToTargetMCMap, pandora::CaloHitList &selectedCaloHitList, const bool selectInputHits, const float maxPhotonPropagation)
Select a subset of calo hits representing those that represent &quot;reconstructable&quot; regions of the event...