LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
CosmicRayTaggingTool.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 
12 
15 
17 
18 using namespace pandora;
19 
20 namespace lar_content
21 {
22 
23 CosmicRayTaggingTool::CosmicRayTaggingTool() :
24  m_cutMode("nominal"),
25  m_angularUncertainty(5.f),
26  m_positionalUncertainty(3.f),
27  m_maxAssociationDist(3.f * 18.f),
28  m_minimumHits(15),
29  m_inTimeMargin(5.f),
30  m_inTimeMaxX0(1.f),
31  m_marginY(20.f),
32  m_marginZ(10.f),
33  m_maxNeutrinoCosTheta(0.2f),
34  m_minCosmicCosTheta(0.6f),
35  m_maxCosmicCurvature(0.04f),
36  m_face_Xa(0.f),
37  m_face_Xc(0.f),
38  m_face_Yb(0.f),
39  m_face_Yt(0.f),
40  m_face_Zu(0.f),
41  m_face_Zd(0.f)
42 {
43 }
44 
45 //------------------------------------------------------------------------------------------------------------------------------------------
46 
48 {
49  if ("nominal" == m_cutMode)
50  {
51  // Nominal values set in constructor
52  }
53  else if ("cautious" == m_cutMode)
54  {
57  }
58  else if ("aggressive" == m_cutMode)
59  {
60  m_minCosmicCosTheta = 0.6f;
61  m_maxCosmicCurvature = 0.1f;
62  }
63  else
64  {
65  std::cout << "CosmicRayTaggingTool - invalid cut mode " << m_cutMode << std::endl;
66  return STATUS_CODE_INVALID_PARAMETER;
67  }
68 
69  return STATUS_CODE_SUCCESS;
70 }
71 
72 //------------------------------------------------------------------------------------------------------------------------------------------
73 
74 void CosmicRayTaggingTool::FindAmbiguousPfos(const PfoList &parentCosmicRayPfos, PfoList &ambiguousPfos, const MasterAlgorithm *const /*pAlgorithm*/)
75 {
76  if (this->GetPandora().GetSettings()->ShouldDisplayAlgorithmInfo())
77  std::cout << "----> Running Algorithm Tool: " << this->GetInstanceName() << ", " << this->GetType() << std::endl;
78 
79  // TODO First time only, TODO Refactor with master algorithm
80  const LArTPCMap &larTPCMap(this->GetPandora().GetGeometry()->GetLArTPCMap());
81  const LArTPC *const pFirstLArTPC(larTPCMap.begin()->second);
82 
83  float parentMinX(pFirstLArTPC->GetCenterX() - 0.5f * pFirstLArTPC->GetWidthX());
84  float parentMaxX(pFirstLArTPC->GetCenterX() + 0.5f * pFirstLArTPC->GetWidthX());
85  float parentMinY(pFirstLArTPC->GetCenterY() - 0.5f * pFirstLArTPC->GetWidthY());
86  float parentMaxY(pFirstLArTPC->GetCenterY() + 0.5f * pFirstLArTPC->GetWidthY());
87  float parentMinZ(pFirstLArTPC->GetCenterZ() - 0.5f * pFirstLArTPC->GetWidthZ());
88  float parentMaxZ(pFirstLArTPC->GetCenterZ() + 0.5f * pFirstLArTPC->GetWidthZ());
89 
90  for (const LArTPCMap::value_type &mapEntry : larTPCMap)
91  {
92  const LArTPC *const pLArTPC(mapEntry.second);
93  parentMinX = std::min(parentMinX, pLArTPC->GetCenterX() - 0.5f * pLArTPC->GetWidthX());
94  parentMaxX = std::max(parentMaxX, pLArTPC->GetCenterX() + 0.5f * pLArTPC->GetWidthX());
95  parentMinY = std::min(parentMinY, pLArTPC->GetCenterY() - 0.5f * pLArTPC->GetWidthY());
96  parentMaxY = std::max(parentMaxY, pLArTPC->GetCenterY() + 0.5f * pLArTPC->GetWidthY());
97  parentMinZ = std::min(parentMinZ, pLArTPC->GetCenterZ() - 0.5f * pLArTPC->GetWidthZ());
98  parentMaxZ = std::max(parentMaxZ, pLArTPC->GetCenterZ() + 0.5f * pLArTPC->GetWidthZ());
99  }
100 
101  m_face_Xa = parentMinX;
102  m_face_Xc = parentMaxX;
103  m_face_Yb = parentMinY;
104  m_face_Yt = parentMaxY;
105  m_face_Zu = parentMinZ;
106  m_face_Zd = parentMaxZ;
107 
108  PfoToPfoListMap pfoAssociationMap;
109  this->GetPfoAssociations(parentCosmicRayPfos, pfoAssociationMap);
110 
111  PfoToSliceIdMap pfoToSliceIdMap;
112  this->SliceEvent(parentCosmicRayPfos, pfoAssociationMap, pfoToSliceIdMap);
113 
114  CRCandidateList candidates;
115  this->GetCRCandidates(parentCosmicRayPfos, pfoToSliceIdMap, candidates);
116 
117  PfoToBoolMap pfoToInTimeMap;
118  this->CheckIfInTime(candidates, pfoToInTimeMap);
119 
120  PfoToBoolMap pfoToIsContainedMap;
121  this->CheckIfContained(candidates, pfoToIsContainedMap );
122 
123  PfoToBoolMap pfoToIsTopToBottomMap;
124  this->CheckIfTopToBottom(candidates, pfoToIsTopToBottomMap);
125 
126  UIntSet neutrinoSliceSet;
127  this->GetNeutrinoSlices(candidates, pfoToInTimeMap, pfoToIsContainedMap, neutrinoSliceSet);
128 
129  PfoToBoolMap pfoToIsLikelyCRMuonMap;
130  this->TagCRMuons(candidates, pfoToInTimeMap, pfoToIsTopToBottomMap, neutrinoSliceSet, pfoToIsLikelyCRMuonMap);
131 
132  for (const ParticleFlowObject *const pPfo : parentCosmicRayPfos)
133  {
134  if (!pfoToIsLikelyCRMuonMap.at(pPfo))
135  ambiguousPfos.push_back(pPfo);
136  }
137 }
138 
139 //------------------------------------------------------------------------------------------------------------------------------------------
140 
141 bool CosmicRayTaggingTool::GetValid3DCluster(const ParticleFlowObject *const pPfo, const Cluster *&pCluster3D) const
142 {
143  pCluster3D = nullptr;
144  ClusterList clusters3D;
145  LArPfoHelper::GetThreeDClusterList(pPfo, clusters3D);
146 
147  // ATTN Only uses first cluster with hits of type TPC_3D
148  if (clusters3D.empty() || (clusters3D.front()->GetNCaloHits() < m_minimumHits))
149  return false;
150 
151  pCluster3D = clusters3D.front();
152  return true;
153 }
154 
155 //------------------------------------------------------------------------------------------------------------------------------------------
156 
157 void CosmicRayTaggingTool::GetPfoAssociations(const PfoList &parentCosmicRayPfos, PfoToPfoListMap &pfoAssociationMap) const
158 {
159  // ATTN If wire w pitches vary between TPCs, exception will be raised in initialisation of lar pseudolayer plugin
160  const LArTPC *const pFirstLArTPC(this->GetPandora().GetGeometry()->GetLArTPCMap().begin()->second);
161  const float layerPitch(pFirstLArTPC->GetWirePitchW());
162 
163  PfoToSlidingFitsMap pfoToSlidingFitsMap;
164 
165  for (const ParticleFlowObject *const pPfo : parentCosmicRayPfos)
166  {
167  const pandora::Cluster *pCluster(nullptr);
168  if (!this->GetValid3DCluster(pPfo, pCluster) || !pCluster)
169  continue;
170 
171  (void) pfoToSlidingFitsMap.insert(PfoToSlidingFitsMap::value_type(pPfo, std::make_pair(
172  ThreeDSlidingFitResult(pCluster, 5, layerPitch), ThreeDSlidingFitResult(pCluster, 100, layerPitch)))); // TODO Configurable
173  }
174 
175  for (const ParticleFlowObject *const pPfo1 : parentCosmicRayPfos)
176  {
177  PfoToSlidingFitsMap::const_iterator iter1(pfoToSlidingFitsMap.find(pPfo1));
178  if (pfoToSlidingFitsMap.end() == iter1)
179  continue;
180 
181  const ThreeDSlidingFitResult &fitPos1(iter1->second.first), &fitDir1(iter1->second.second);
182 
183  for (const ParticleFlowObject *const pPfo2 : parentCosmicRayPfos)
184  {
185  if (pPfo1 == pPfo2)
186  continue;
187 
188  PfoToSlidingFitsMap::const_iterator iter2(pfoToSlidingFitsMap.find(pPfo2));
189  if (pfoToSlidingFitsMap.end() == iter2)
190  continue;
191 
192  const ThreeDSlidingFitResult &fitPos2(iter2->second.first), &fitDir2(iter2->second.second);
193 
194  // TODO Use existing LArPointingClusters and IsEmission/IsNode logic, for consistency
195  if (!(this->CheckAssociation(fitPos1.GetGlobalMinLayerPosition(), fitDir1.GetGlobalMinLayerDirection() * -1.f, fitPos2.GetGlobalMinLayerPosition(), fitDir2.GetGlobalMinLayerDirection() * -1.f) ||
196  this->CheckAssociation(fitPos1.GetGlobalMinLayerPosition(), fitDir1.GetGlobalMinLayerDirection() * -1.f, fitPos2.GetGlobalMaxLayerPosition(), fitDir2.GetGlobalMaxLayerDirection()) ||
197  this->CheckAssociation(fitPos1.GetGlobalMaxLayerPosition(), fitDir1.GetGlobalMaxLayerDirection(), fitPos2.GetGlobalMinLayerPosition(), fitDir2.GetGlobalMinLayerDirection() * -1.f) ||
198  this->CheckAssociation(fitPos1.GetGlobalMaxLayerPosition(), fitDir1.GetGlobalMaxLayerDirection(), fitPos2.GetGlobalMaxLayerPosition(), fitDir2.GetGlobalMaxLayerDirection())))
199  {
200  continue;
201  }
202 
203  PfoList &pfoList1(pfoAssociationMap[pPfo1]), &pfoList2(pfoAssociationMap[pPfo2]);
204 
205  if (pfoList1.end() == std::find(pfoList1.begin(), pfoList1.end(), pPfo2))
206  pfoList1.push_back(pPfo2);
207 
208  if (pfoList2.end() == std::find(pfoList2.begin(), pfoList2.end(), pPfo1))
209  pfoList2.push_back(pPfo1);
210  }
211  }
212 }
213 
214 //------------------------------------------------------------------------------------------------------------------------------------------
215 
216 bool CosmicRayTaggingTool::CheckAssociation(const CartesianVector &endPoint1, const CartesianVector &endDir1, const CartesianVector &endPoint2,
217  const CartesianVector &endDir2) const
218 {
219  // TODO This function needs significant tidying and checks (variable names must be self describing, check deltaTheta, etc.)
220  const CartesianVector n(endDir1.GetUnitVector());
221  const CartesianVector m(endDir2.GetUnitVector());
222  const CartesianVector a(endPoint2 - endPoint1);
223  const float b(n.GetDotProduct(m));
224 
225  // Parallel lines never meet
226  if (std::fabs(b - 1.f) < std::numeric_limits<float>::epsilon())
227  return false;
228 
229  // Distance from endPoint1 along endDir1 to the point of closest approach
230  const float lambda((n - m * b).GetDotProduct(a) / (1.f - b * b));
231 
232  // Distance from endPoint2 along endDir2 to the point of closest approach
233  const float mu((n * b - m).GetDotProduct(a) / (1.f - b * b));
234 
235  // Calculate the maximum "vertex uncertainty"
236  const float deltaTheta(m_angularUncertainty * M_PI / 180.f);
237  const float maxVertexUncertainty(m_maxAssociationDist * std::sin(deltaTheta) + m_positionalUncertainty);
238 
239  // Ensure that the distances to the point of closest approch are within the limits
240  if ((lambda < -maxVertexUncertainty) || (mu < -maxVertexUncertainty) ||
241  (lambda > m_maxAssociationDist + maxVertexUncertainty) || (mu > m_maxAssociationDist + maxVertexUncertainty))
242  {
243  return false;
244  }
245 
246  // Ensure point of closest approach is within detector
247  const CartesianVector impactPosition((endPoint1 + n * lambda + endPoint2 + m * mu) * 0.5f);
248 
249  if ((impactPosition.GetX() < m_face_Xa - maxVertexUncertainty) || (impactPosition.GetX() > m_face_Xc + maxVertexUncertainty) ||
250  (impactPosition.GetY() < m_face_Yb - maxVertexUncertainty) || (impactPosition.GetY() > m_face_Yt + maxVertexUncertainty) ||
251  (impactPosition.GetZ() < m_face_Zu - maxVertexUncertainty) || (impactPosition.GetZ() > m_face_Zd + maxVertexUncertainty))
252  {
253  return false;
254  }
255 
256  // Compare distance of closest approach and max uncertainty in impact parameter
257  const float maxImpactDist(std::sin(deltaTheta) * (std::fabs(mu) + std::fabs(lambda)) + m_positionalUncertainty);
258  const CartesianVector d(a - n * lambda + m * mu);
259 
260  return (d.GetMagnitude() < maxImpactDist);
261 }
262 
263 //------------------------------------------------------------------------------------------------------------------------------------------
264 
265 void CosmicRayTaggingTool::SliceEvent(const PfoList &parentCosmicRayPfos, const PfoToPfoListMap &pfoAssociationMap, PfoToSliceIdMap &pfoToSliceIdMap) const
266 {
267  SliceList sliceList;
268 
269  for (const ParticleFlowObject *const pPfo : parentCosmicRayPfos)
270  {
271  bool isAlreadyInSlice(false);
272 
273  for (const PfoList &slice : sliceList)
274  {
275  if (std::find(slice.begin(), slice.end(), pPfo) != slice.end())
276  {
277  isAlreadyInSlice = true;
278  break;
279  }
280  }
281 
282  if (!isAlreadyInSlice)
283  {
284  sliceList.push_back(PfoList());
285  this->FillSlice(pPfo, pfoAssociationMap, sliceList.back());
286  }
287  }
288 
289  unsigned int sliceId(0);
290  for (const PfoList &slice : sliceList)
291  {
292  for (const ParticleFlowObject *const pPfo : slice)
293  {
294  if (!pfoToSliceIdMap.insert(PfoToSliceIdMap::value_type(pPfo, sliceId)).second)
295  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
296  }
297 
298  ++sliceId;
299  }
300 }
301 
302 //------------------------------------------------------------------------------------------------------------------------------------------
303 
304 void CosmicRayTaggingTool::FillSlice(const ParticleFlowObject *const pPfo, const PfoToPfoListMap &pfoAssociationMap, PfoList &slice) const
305 {
306  if (std::find(slice.begin(), slice.end(), pPfo) != slice.end())
307  return;
308 
309  slice.push_back(pPfo);
310 
311  PfoToPfoListMap::const_iterator iter(pfoAssociationMap.find(pPfo));
312 
313  if (pfoAssociationMap.end() != iter)
314  {
315  for (const ParticleFlowObject *const pAssociatedPfo : iter->second)
316  this->FillSlice(pAssociatedPfo, pfoAssociationMap, slice);
317  }
318 }
319 
320 //------------------------------------------------------------------------------------------------------------------------------------------
321 
322 void CosmicRayTaggingTool::GetCRCandidates(const PfoList &parentCosmicRayPfos, const PfoToSliceIdMap &pfoToSliceIdMap, CRCandidateList &candidates) const
323 {
324  for (const ParticleFlowObject *const pPfo : parentCosmicRayPfos)
325  {
326  if (!LArPfoHelper::IsFinalState(pPfo))
327  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
328 
329  candidates.push_back(CRCandidate(this->GetPandora(), pPfo, pfoToSliceIdMap.at(pPfo)));
330  }
331 }
332 
333 //------------------------------------------------------------------------------------------------------------------------------------------
334 
335 void CosmicRayTaggingTool::CheckIfInTime(const CRCandidateList &candidates, PfoToBoolMap &pfoToInTimeMap) const
336 {
337  const LArTPCMap &larTPCMap(this->GetPandora().GetGeometry()->GetLArTPCMap());
338 
339  for (const CRCandidate &candidate : candidates)
340  {
341  // Cosmic-ray muons extending outside of (single) physical volume if given t0 is that of the beam particle
343 
344  if (candidate.m_canFit)
345  {
346  minX = ((candidate.m_endPoint1.GetX() < candidate.m_endPoint2.GetX()) ? candidate.m_endPoint1.GetX() : candidate.m_endPoint2.GetX());
347  maxX = ((candidate.m_endPoint1.GetX() > candidate.m_endPoint2.GetX()) ? candidate.m_endPoint1.GetX() : candidate.m_endPoint2.GetX());
348  }
349  else
350  {
351  // Handle any particles with small numbers of 3D hits, for which no 3D sliding fit information is available
352  for (const Cluster *const pCluster : candidate.m_pPfo->GetClusterList())
353  {
354  float clusterMinX(std::numeric_limits<float>::max()), clusterMaxX(-std::numeric_limits<float>::max());
355  LArClusterHelper::GetClusterSpanX(pCluster, clusterMinX, clusterMaxX);
356  minX = std::min(clusterMinX, minX);
357  maxX = std::max(clusterMaxX, maxX);
358  }
359  }
360 
361  bool isInTime((minX > m_face_Xa - m_inTimeMargin) && (maxX < m_face_Xc + m_inTimeMargin));
362 
363  // Cosmic-ray muons that have been shifted and stitched across mid plane between volumes
364  if (isInTime)
365  {
366  try
367  {
368  if (std::fabs(LArPfoHelper::GetVertex(candidate.m_pPfo)->GetX0()) > m_inTimeMaxX0)
369  isInTime = false;
370  }
371  catch (const StatusCodeException &) {}
372  }
373 
374  // Cosmic-ray muons extending outside of (any individual) physical volume if given t0 is that of the beam particle
375  if (isInTime)
376  {
377  CaloHitList caloHitList;
378  LArPfoHelper::GetCaloHits(candidate.m_pPfo, TPC_VIEW_U, caloHitList);
379  LArPfoHelper::GetCaloHits(candidate.m_pPfo, TPC_VIEW_V, caloHitList);
380  LArPfoHelper::GetCaloHits(candidate.m_pPfo, TPC_VIEW_W, caloHitList);
381 
382  bool isFirstHit(true);
383  bool isInSingleVolume(true);
384  unsigned int volumeId(std::numeric_limits<unsigned int>::max());
385 
386  for (const CaloHit *const pCaloHit : caloHitList)
387  {
388  const LArCaloHit *const pLArCaloHit(dynamic_cast<const LArCaloHit*>(pCaloHit));
389 
390  if (!pLArCaloHit)
391  continue;
392 
393  if (isFirstHit)
394  {
395  isFirstHit = false;
396  volumeId = pLArCaloHit->GetLArTPCVolumeId();
397  }
398  else if (volumeId != pLArCaloHit->GetLArTPCVolumeId())
399  {
400  isInSingleVolume = false;
401  break;
402  }
403  }
404 
405  LArTPCMap::const_iterator tpcIter(larTPCMap.find(volumeId));
406 
407  if (isInSingleVolume && (larTPCMap.end() != tpcIter))
408  {
409  const float thisFaceXLow(tpcIter->second->GetCenterX() - 0.5f * tpcIter->second->GetWidthX());
410  const float thisFaceXHigh(tpcIter->second->GetCenterX() + 0.5f * tpcIter->second->GetWidthX());
411 
412  if (!((minX > thisFaceXLow - m_inTimeMargin) && (maxX < thisFaceXHigh + m_inTimeMargin)))
413  isInTime = false;
414  }
415  }
416 
417  if (!pfoToInTimeMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, isInTime)).second)
418  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
419  }
420 }
421 
422 //------------------------------------------------------------------------------------------------------------------------------------------
423 
424 void CosmicRayTaggingTool::CheckIfContained(const CRCandidateList &candidates, PfoToBoolMap &pfoToIsContainedMap) const
425 {
426  for (const CRCandidate &candidate : candidates)
427  {
428  const float upperY((candidate.m_endPoint1.GetY() > candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
429  const float lowerY((candidate.m_endPoint1.GetY() < candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
430 
431  const float zAtUpperY((candidate.m_endPoint1.GetY() > candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetZ() : candidate.m_endPoint2.GetZ());
432  const float zAtLowerY((candidate.m_endPoint1.GetY() < candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetZ() : candidate.m_endPoint2.GetZ());
433 
434  const bool isContained((upperY < m_face_Yt - m_marginY) && (upperY > m_face_Yb + m_marginY) &&
435  (lowerY < m_face_Yt - m_marginY) && (lowerY > m_face_Yb + m_marginY) &&
436  (zAtUpperY < m_face_Zd - m_marginZ) && (zAtUpperY > m_face_Zu + m_marginZ) &&
437  (zAtLowerY < m_face_Zd - m_marginZ) && (zAtLowerY > m_face_Zu + m_marginZ));
438 
439  if (!pfoToIsContainedMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, isContained)).second)
440  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
441  }
442 }
443 
444 //------------------------------------------------------------------------------------------------------------------------------------------
445 
446 void CosmicRayTaggingTool::CheckIfTopToBottom(const CRCandidateList &candidates, PfoToBoolMap &pfoToIsTopToBottomMap) const
447 {
448  for (const CRCandidate &candidate : candidates)
449  {
450  const float upperY((candidate.m_endPoint1.GetY() > candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
451  const float lowerY((candidate.m_endPoint1.GetY() < candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
452 
453  const bool isTopToBottom((upperY > m_face_Yt - m_marginY) && (lowerY < m_face_Yb + m_marginY));
454 
455  if (!pfoToIsTopToBottomMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, isTopToBottom)).second)
456  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
457  }
458 }
459 
460 //------------------------------------------------------------------------------------------------------------------------------------------
461 
462 void CosmicRayTaggingTool::GetNeutrinoSlices(const CRCandidateList &candidates, const PfoToBoolMap &pfoToInTimeMap, const PfoToBoolMap &pfoToIsContainedMap,
463  UIntSet &neutrinoSliceSet) const
464 {
465  for (const CRCandidate &candidate : candidates)
466  {
467  if (neutrinoSliceSet.count(candidate.m_sliceId))
468  continue;
469 
470  const bool likelyNeutrino(candidate.m_canFit && pfoToInTimeMap.at(candidate.m_pPfo) &&
471  (candidate.m_theta < m_maxNeutrinoCosTheta || pfoToIsContainedMap.at(candidate.m_pPfo)));
472 
473  if (likelyNeutrino)
474  (void) neutrinoSliceSet.insert(candidate.m_sliceId);
475  }
476 }
477 
478 //------------------------------------------------------------------------------------------------------------------------------------------
479 
480 void CosmicRayTaggingTool::TagCRMuons(const CRCandidateList &candidates, const PfoToBoolMap &pfoToInTimeMap, const PfoToBoolMap &pfoToIsTopToBottomMap,
481  const UIntSet &neutrinoSliceSet, PfoToBoolMap &pfoToIsLikelyCRMuonMap) const
482 {
483  for (const CRCandidate &candidate : candidates)
484  {
485  const bool likelyCRMuon(!neutrinoSliceSet.count(candidate.m_sliceId) && (!pfoToInTimeMap.at(candidate.m_pPfo) || (candidate.m_canFit &&
486  (pfoToIsTopToBottomMap.at(candidate.m_pPfo) || ((candidate.m_theta > m_minCosmicCosTheta) && (candidate.m_curvature < m_maxCosmicCurvature)))) ));
487 
488  if (!pfoToIsLikelyCRMuonMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, likelyCRMuon)).second)
489  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
490  }
491 }
492 
493 //------------------------------------------------------------------------------------------------------------------------------------------
494 //------------------------------------------------------------------------------------------------------------------------------------------
495 
496 CosmicRayTaggingTool::CRCandidate::CRCandidate(const Pandora &pandora, const ParticleFlowObject *const pPfo, const unsigned int sliceId) :
497  m_pPfo(pPfo),
498  m_sliceId(sliceId),
499  m_canFit(false),
500  m_endPoint1(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
501  m_endPoint2(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
502  m_length(std::numeric_limits<float>::max()),
503  m_curvature(std::numeric_limits<float>::max()),
504  m_theta(std::numeric_limits<float>::max())
505 {
506  ClusterList clusters3D;
507  LArPfoHelper::GetThreeDClusterList(pPfo, clusters3D);
508 
509  if (!clusters3D.empty() && (clusters3D.front()->GetNCaloHits() > 15)) // TODO Configurable
510  {
511  m_canFit = true;
512  const LArTPC *const pFirstLArTPC(pandora.GetGeometry()->GetLArTPCMap().begin()->second);
513  const ThreeDSlidingFitResult slidingFitResult(clusters3D.front(), 5, pFirstLArTPC->GetWirePitchW()); // TODO Configurable
514  this->CalculateFitVariables(slidingFitResult);
515  }
516 }
517 
518 //------------------------------------------------------------------------------------------------------------------------------------------
519 
521 {
522  m_endPoint1 = slidingFitResult.GetGlobalMinLayerPosition();
523  m_endPoint2 = slidingFitResult.GetGlobalMaxLayerPosition();
524  m_length = (m_endPoint2 - m_endPoint1).GetMagnitude();
525 
526  if (std::fabs(m_length) > std::numeric_limits<float>::epsilon())
527  m_theta = std::fabs(m_endPoint2.GetY() - m_endPoint1.GetY()) / m_length;
528 
529  const float layerPitch(slidingFitResult.GetFirstFitResult().GetLayerPitch());
530 
531  CartesianPointVector directionList;
532  for (int i = slidingFitResult.GetMinLayer(); i < slidingFitResult.GetMaxLayer(); ++i)
533  {
534  CartesianVector direction(0.f, 0.f, 0.f);
535  if (STATUS_CODE_SUCCESS == slidingFitResult.GetGlobalFitDirection(static_cast<float>(i) * layerPitch, direction))
536  directionList.push_back(direction);
537  }
538 
539  CartesianVector meanDirection(0.f, 0.f, 0.f);
540  for (const CartesianVector &direction : directionList)
541  meanDirection += direction;
542 
543  if (!directionList.empty() > 0)
544  meanDirection *= 1.f / static_cast<float>(directionList.size());
545 
546  m_curvature = 0.f;
547  for (const CartesianVector &direction : directionList)
548  m_curvature += (direction - meanDirection).GetMagnitude();
549 
550  if (!directionList.empty() > 0)
551  m_curvature *= 1.f / static_cast<float>(directionList.size());
552 }
553 
554 //------------------------------------------------------------------------------------------------------------------------------------------
555 
556 StatusCode CosmicRayTaggingTool::ReadSettings(const TiXmlHandle xmlHandle)
557 {
558  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
559  "CutMode", m_cutMode));
560  std::transform(m_cutMode.begin(), m_cutMode.end(), m_cutMode.begin(), ::tolower);
561 
562  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
563  "AngularUncertainty", m_angularUncertainty ));
564 
565  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
566  "PositionalUncertainty", m_positionalUncertainty ));
567 
568  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
569  "MaxAssociationDist", m_maxAssociationDist));
570 
571  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
572  "HitThreshold", m_minimumHits));
573 
574  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
575  "InTimeMargin", m_inTimeMargin));
576 
577  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
578  "InTimeMaxX0", m_inTimeMaxX0));
579 
580  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
581  "MarginY", m_marginY));
582 
583  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
584  "MarginZ", m_marginZ));
585 
586  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
587  "MaxNeutrinoCosTheta", m_maxNeutrinoCosTheta));
588 
589  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
590  "MinCosmicCosTheta", m_minCosmicCosTheta));
591 
592  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
593  "MaxCosmicCurvature", m_maxCosmicCurvature));
594 
595  return STATUS_CODE_SUCCESS;
596 }
597 
598 } // namespace lar_content
std::unordered_map< const pandora::ParticleFlowObject *, pandora::PfoList > PfoToPfoListMap
std::unordered_map< const pandora::ParticleFlowObject *, bool > PfoToBoolMap
void FindAmbiguousPfos(const pandora::PfoList &parentCosmicRayPfos, pandora::PfoList &ambiguousPfos, const MasterAlgorithm *const pAlgorithm)
Find the list of ambiguous pfos (could represent cosmic-ray muons or neutrinos)
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
void TagCRMuons(const CRCandidateList &candidates, const PfoToBoolMap &pfoToInTimeMap, const PfoToBoolMap &pfoToIsTopToBottomMap, const UIntSet &neutrinoSliceSet, PfoToBoolMap &pfoToIsLikelyCRMuonMap) const
Tag Pfos which are likely to be a CR muon.
Header file for the pfo helper class.
std::unordered_map< const pandora::ParticleFlowObject *, unsigned int > PfoToSliceIdMap
float m_maxNeutrinoCosTheta
The maximum cos(theta) that a Pfo can have to be classified as a likely neutrino. ...
const pandora::CartesianVector & GetGlobalMinLayerDirection() const
Get global direction corresponding to the fit result in minimum fit layer.
Header file for the lar calo hit class.
void SliceEvent(const pandora::PfoList &parentCosmicRayPfos, const PfoToPfoListMap &pfoAssociationMap, PfoToSliceIdMap &pfoToSliceIdMap) const
Break the event up into slices of associated Pfos.
bool CheckAssociation(const pandora::CartesianVector &endPoint1, const pandora::CartesianVector &endDir1, const pandora::CartesianVector &endPoint2, const pandora::CartesianVector &endDir2) const
Check whethe two Pfo endpoints are associated by distance of closest approach.
float m_positionalUncertainty
The uncertainty in cm for the position of Pfo endpoint in 3D.
void CheckIfTopToBottom(const CRCandidateList &candidates, PfoToBoolMap &pfoToIsTopToBottomMap) const
Check if each candidate is "top to bottom".
int GetMaxLayer() const
Get the maximum occupied layer in the sliding fit.
static const pandora::Vertex * GetVertex(const pandora::ParticleFlowObject *const pPfo)
Get the pfo vertex.
double m_theta
Direction made with vertical.
bool m_canFit
If there are a sufficient number of 3D hits to perform a fitting.
Class to encapsulate the logic required determine if a Pfo should or shouldn&#39;t be tagged as a cosmic ...
STL namespace.
void CheckIfContained(const CRCandidateList &candidates, PfoToBoolMap &pfoToIsContainedMap) const
Check if each candidate is "contained" (contained = no associations to Y or Z detector faces...
unsigned int GetLArTPCVolumeId() const
Get the lar tpc volume id.
Definition: LArCaloHit.h:110
float m_angularUncertainty
The uncertainty in degrees for the angle of a Pfo.
LAr calo hit class.
Definition: LArCaloHit.h:38
CRCandidate(const pandora::Pandora &pandora, const pandora::ParticleFlowObject *const pPfo, const unsigned int sliceId)
Constructor.
void FillSlice(const pandora::ParticleFlowObject *const pPfo, const PfoToPfoListMap &pfoAssociationMap, pandora::PfoList &slice) const
Fill a slice iteratively using Pfo associations.
std::string m_cutMode
Choose a set of cuts using a keyword - "cautious" = remove as few neutrinos as possible "nominal" = o...
std::list< CRCandidate > CRCandidateList
TFile f
Definition: plotHisto.C:6
double m_length
Straight line length of the linear fit.
Int_t max
Definition: plot.C:27
float m_inTimeMaxX0
The maximum pfo x0 (determined from shifted vertex) to allow pfo to still be considered in time...
intermediate_table::const_iterator const_iterator
Header file for the cluster helper class.
void CheckIfInTime(const CRCandidateList &candidates, PfoToBoolMap &pfoToInTimeMap) const
Check if each candidate is "in time".
pandora::CartesianVector m_endPoint2
Second fitted end point in 3D.
const pandora::CartesianVector & GetGlobalMaxLayerDirection() const
Get global direction corresponding to the fit result in maximum fit layer.
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
float m_inTimeMargin
The maximum distance outside of the physical detector volume that a Pfo may be to still be considered...
double m_curvature
Measure of the curvature of the track.
pandora::StatusCode GetGlobalFitDirection(const float rL, pandora::CartesianVector &direction) const
Get global fit direction for a given longitudinal coordinate.
Float_t d
Definition: plot.C:237
void CalculateFitVariables(const ThreeDSlidingFitResult &slidingFitResult)
Calculate all variables which require a fit.
float GetLayerPitch() const
Get the layer pitch, units cm.
void GetPfoAssociations(const pandora::PfoList &parentCosmicRayPfos, PfoToPfoListMap &pfoAssociationMap) const
Get mapping between Pfos that are associated with it other by pointing.
int GetMinLayer() const
Get the minimum occupied layer in the sliding fit.
static void GetThreeDClusterList(const pandora::ParticleFlowObject *const pPfo, pandora::ClusterList &clusterList)
Get the list of 3D clusters from an input pfo.
const pandora::CartesianVector & GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
float m_minCosmicCosTheta
The minimum cos(theta) that a Pfo can have to be classified as a likely CR muon.
bool GetValid3DCluster(const pandora::ParticleFlowObject *const pPfo, const pandora::Cluster *&pCluster3D) const
Get the 3D calo hit cluster associated with a given Pfo, and check if it has sufficient hits...
std::vector< pandora::PfoList > SliceList
float m_marginZ
The minimum distance from a detector Z-face for a Pfo to be associated.
float m_marginY
The minimum distance from a detector Y-face for a Pfo to be associated.
float m_maxCosmicCurvature
The maximum curvature that a Pfo can have to be classified as a likely CR muon.
Int_t min
Definition: plot.C:26
MasterAlgorithm class.
static bool IsFinalState(const pandora::ParticleFlowObject *const pPfo)
Whether a pfo is a primary parent particle.
const TwoDSlidingFitResult & GetFirstFitResult() const
Get the first sliding fit result for this cluster.
static void GetClusterSpanX(const pandora::Cluster *const pCluster, float &xmin, float &xmax)
Get minimum and maximum X positions of the calo hits in a cluster.
Header file for the cosmic-ray tagging tool class.
float m_maxAssociationDist
The maximum distance from endpoint to point of closest approach, typically a multiple of LAr radiatio...
Char_t n[5]
unsigned int m_minimumHits
The minimum number of hits for a Pfo to be considered.
std::unordered_map< const pandora::ParticleFlowObject *, SlidingFitPair > PfoToSlidingFitsMap
void GetCRCandidates(const pandora::PfoList &parentCosmicRayPfos, const PfoToSliceIdMap &pfoToSliceIdMap, CRCandidateList &candidates) const
Make a list of CRCandidates.
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 GetNeutrinoSlices(const CRCandidateList &candidates, const PfoToBoolMap &pfoToInTimeMap, const PfoToBoolMap &pfoToIsContainedMap, UIntSet &neutrinoSliceSet) const
Get the slice indices which contain a likely neutrino Pfo.
const pandora::CartesianVector & GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.
pandora::CartesianVector m_endPoint1
First fitted end point in 3D.