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 "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
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 -> pair (reconstructable MCparticles, number of reconstructable 2D hits sha...
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
intermediate_table::const_iterator const_iterator
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 "good" calo hits that can be use to d...
std::vector< art::Ptr< simb::MCParticle > > MCParticleVector
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.
weight
Definition: test.py:257
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
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...
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
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.
const double a
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
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.
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)
static bool * b
Definition: config.cpp:1043
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.
void function(int client, int *resource, int parblock, int *test, int p)
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.
std::map< const pandora::ParticleFlowObject *, MCParticleToSharedHitsVector > PfoToMCParticleHitSharingMap
static bool IsNeutrino(const pandora::MCParticle *const pMCParticle)
Whether a mc particle is a neutrino or antineutrino.
QTextStream & endl(QTextStream &s)
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 "reconstructable" regions of the event...