LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
TransverseAssociationAlgorithm.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 
13 
15 
17 
18 using namespace pandora;
19 
20 namespace lar_content
21 {
22 
23 TransverseAssociationAlgorithm::TransverseAssociationAlgorithm() :
24  m_firstLengthCut(1.5f),
25  m_secondLengthCut(7.5f),
26  m_clusterWindow(3.f),
27  m_clusterAngle(45.f),
28  m_clusterCosAngle(std::cos(m_clusterAngle * M_PI / 180.f)),
29  m_clusterTanAngle(std::tan(m_clusterAngle * M_PI / 180.f)),
30  m_maxTransverseOverlap(0.5f),
31  m_maxProjectedOverlap(1.f),
32  m_maxLongitudinalOverlap(1.5f),
33  m_transverseClusterMinCosTheta(0.866f),
34  m_transverseClusterMinLength(0.5f),
35  m_transverseClusterMaxDisplacement(1.5f),
36  m_searchRegionX(3.5f),
37  m_searchRegionZ(2.f)
38 {
39 }
40 
41 //------------------------------------------------------------------------------------------------------------------------------------------
42 
43 void TransverseAssociationAlgorithm::GetListOfCleanClusters(const ClusterList *const pClusterList, ClusterVector &clusterVector) const
44 {
45  clusterVector.clear();
46  clusterVector.insert(clusterVector.begin(), pClusterList->begin(), pClusterList->end());
47  std::sort(clusterVector.begin(), clusterVector.end(), LArClusterHelper::SortByNHits);
48 }
49 
50 //------------------------------------------------------------------------------------------------------------------------------------------
51 
53 {
54  TransverseClusterList transverseClusterList;
55 
56  try
57  {
58  ClusterToClustersMap nearbyClusters;
59  this->GetNearbyClusterMap(allClusters, nearbyClusters);
60 
61  if (nearbyClusters.empty())
62  return;
63 
64  // Step 1: Sort the input clusters into sub-samples
65  // (a) shortClusters: below first length cut
66  // (b) mediumClusters: between first and second length cuts (separated into transverse and longitudinal)
67  // (c) longClusters: above second length cut
68  ClusterVector shortClusters, transverseMediumClusters, longitudinalMediumClusters, longClusters;
69  this->SortInputClusters(allClusters, shortClusters, transverseMediumClusters, longitudinalMediumClusters, longClusters);
70 
71  ClusterVector transverseClusters(shortClusters.begin(), shortClusters.end());
72  transverseClusters.insert(transverseClusters.end(), transverseMediumClusters.begin(), transverseMediumClusters.end());
73 
74  ClusterVector establishedClusters(transverseMediumClusters.begin(), transverseMediumClusters.end());
75  establishedClusters.insert(establishedClusters.end(), longitudinalMediumClusters.begin(), longitudinalMediumClusters.end());
76  establishedClusters.insert(establishedClusters.end(), longClusters.begin(), longClusters.end());
77 
78  // Step 2: Form loose transverse associations between short clusters,
79  // without hopping over any established clusters
80  ClusterAssociationMap firstAssociationMap;
81  this->FillReducedAssociationMap(nearbyClusters, shortClusters, establishedClusters, firstAssociationMap);
82 
83  // Step 3: Form transverse cluster objects. Basically, try to assign a direction to each
84  // of the clusters in the 'transverseClusters' list. For the short clusters in
85  // this list, the direction is obtained from a straight line fit to its associated
86  // clusters as selected in the previous step.
87  this->FillTransverseClusterList(nearbyClusters, transverseClusters, firstAssociationMap, transverseClusterList);
88 
89  // Step 4: Form loose transverse associations between transverse clusters
90  // (First, associate medium clusters, without hopping over long clusters
91  // Next, associate all transverse clusters, without hopping over any clusters)
92  ClusterAssociationMap secondAssociationMap;
93  this->FillReducedAssociationMap(nearbyClusters, transverseMediumClusters, longClusters, secondAssociationMap);
94  this->FillReducedAssociationMap(nearbyClusters, transverseClusters, allClusters, secondAssociationMap);
95 
96  // Step 5: Form associations between transverse cluster objects
97  // (These transverse associations must already exist as loose associations
98  // between transverse clusters as identified in the previous step).
99  ClusterAssociationMap transverseAssociationMap;
100  this->FillTransverseAssociationMap(nearbyClusters, transverseClusterList, secondAssociationMap, transverseAssociationMap);
101 
102  // Step 6: Finalise the forward/backward transverse associations by symmetrising the
103  // transverse association map and removing any double-counting
104  this->FinalizeClusterAssociationMap(transverseAssociationMap, clusterAssociationMap);
105  }
106  catch (StatusCodeException &statusCodeException)
107  {
108  std::cout << "TransverseAssociationAlgorithm: exception " << statusCodeException.ToString() << std::endl;
109  }
110 
111  for (TransverseClusterList::const_iterator iter = transverseClusterList.begin(), iterEnd = transverseClusterList.end(); iter != iterEnd; ++iter)
112  {
113  delete *iter;
114  }
115 }
116 
117 //------------------------------------------------------------------------------------------------------------------------------------------
118 
120 {
121  HitToClusterMap hitToClusterMap;
122  CaloHitList allCaloHits;
123 
124  for (const Cluster *const pCluster : allClusters)
125  {
126  CaloHitList daughterHits;
127  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
128  allCaloHits.insert(allCaloHits.end(), daughterHits.begin(), daughterHits.end());
129 
130  for (const CaloHit *const pCaloHit : daughterHits)
131  (void)hitToClusterMap.insert(HitToClusterMap::value_type(pCaloHit, pCluster));
132  }
133 
134  HitKDTree2D kdTree;
135  HitKDNode2DList hitKDNode2DList;
136 
137  KDTreeBox hitsBoundingRegion2D(fill_and_bound_2d_kd_tree(allCaloHits, hitKDNode2DList));
138  kdTree.build(hitKDNode2DList, hitsBoundingRegion2D);
139 
140  for (const Cluster *const pCluster : allClusters)
141  {
142  CaloHitList daughterHits;
143  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
144 
145  for (const CaloHit *const pCaloHit : daughterHits)
146  {
148 
149  HitKDNode2DList found;
150  kdTree.search(searchRegionHits, found);
151 
152  for (const auto &hit : found)
153  (void)nearbyClusters[pCluster].insert(hitToClusterMap.at(hit.data));
154  }
155  }
156 }
157 
158 //------------------------------------------------------------------------------------------------------------------------------------------
159 
161  ClusterVector &transverseMediumVector, ClusterVector &longitudinalMediumVector, ClusterVector &longVector) const
162 {
163  for (ClusterVector::const_iterator iter = inputVector.begin(), iterEnd = inputVector.end(); iter != iterEnd; ++iter)
164  {
165  const Cluster *const pCluster = *iter;
166 
167  const float clusterLengthT(this->GetTransverseSpan(pCluster));
168  const float clusterLengthL(this->GetLongitudinalSpan(pCluster));
169  const float clusterLengthSquared(clusterLengthT * clusterLengthT + clusterLengthL * clusterLengthL);
170 
171  if (clusterLengthSquared < m_firstLengthCut * m_firstLengthCut)
172  {
173  shortVector.push_back(pCluster);
174  }
175  else if (clusterLengthSquared < m_secondLengthCut * m_secondLengthCut)
176  {
177  if (clusterLengthL < clusterLengthT * std::fabs(m_clusterTanAngle))
178  transverseMediumVector.push_back(pCluster);
179  else
180  longitudinalMediumVector.push_back(pCluster);
181  }
182  else
183  {
184  longVector.push_back(pCluster);
185  }
186  }
187 
188  std::sort(shortVector.begin(), shortVector.end(), LArClusterHelper::SortByNHits);
189  std::sort(transverseMediumVector.begin(), transverseMediumVector.end(), LArClusterHelper::SortByNHits);
190  std::sort(longitudinalMediumVector.begin(), longitudinalMediumVector.end(), LArClusterHelper::SortByNHits);
191  std::sort(longVector.begin(), longVector.end(), LArClusterHelper::SortByNHits);
192 }
193 
194 //------------------------------------------------------------------------------------------------------------------------------------------
195 
197  const ClusterVector &secondVector, ClusterAssociationMap &clusterAssociationMap) const
198 {
199  // To build a 'reduced' association map, form associations between clusters in the first cluster vector,
200  // but prevent these associations from hopping over any clusters in the second cluster vector.
201  // i.e. A->B from the first vector is forbidden if A->C->B exists with C from the second vector
202 
203  ClusterAssociationMap firstAssociationMap, firstAssociationMapSwapped;
204  ClusterAssociationMap secondAssociationMap, secondAssociationMapSwapped;
205 
206  this->FillAssociationMap(nearbyClusters, firstVector, firstVector, firstAssociationMap, firstAssociationMapSwapped);
207  this->FillAssociationMap(nearbyClusters, firstVector, secondVector, secondAssociationMap, secondAssociationMapSwapped);
208  this->FillReducedAssociationMap(firstAssociationMap, secondAssociationMap, secondAssociationMapSwapped, clusterAssociationMap);
209 }
210 
211 //------------------------------------------------------------------------------------------------------------------------------------------
212 
214  const ClusterVector &secondVector, ClusterAssociationMap &firstAssociationMap, ClusterAssociationMap &secondAssociationMap) const
215 {
216  for (ClusterVector::const_iterator iterI = firstVector.begin(), iterEndI = firstVector.end(); iterI != iterEndI; ++iterI)
217  {
218  const Cluster *const pClusterI = *iterI;
219 
220  for (ClusterVector::const_iterator iterJ = secondVector.begin(), iterEndJ = secondVector.end(); iterJ != iterEndJ; ++iterJ)
221  {
222  const Cluster *const pClusterJ = *iterJ;
223 
224  if (pClusterI == pClusterJ)
225  continue;
226 
227  if (this->IsAssociated(true, pClusterI, pClusterJ, nearbyClusters))
228  {
229  firstAssociationMap[pClusterI].m_forwardAssociations.insert(pClusterJ);
230  secondAssociationMap[pClusterJ].m_backwardAssociations.insert(pClusterI);
231  }
232 
233  if (this->IsAssociated(false, pClusterI, pClusterJ, nearbyClusters))
234  {
235  firstAssociationMap[pClusterI].m_backwardAssociations.insert(pClusterJ);
236  secondAssociationMap[pClusterJ].m_forwardAssociations.insert(pClusterI);
237  }
238  }
239  }
240 }
241 
242 //------------------------------------------------------------------------------------------------------------------------------------------
243 
245  const ClusterAssociationMap &inputAssociationMap, TransverseClusterList &transverseClusterList) const
246 {
247  for (ClusterVector::const_iterator iter = inputVector.begin(), iterEnd = inputVector.end(); iter != iterEnd; ++iter)
248  {
249  const Cluster *const pCluster = *iter;
250  const float ratio{LArGeometryHelper::GetWirePitchRatio(this->GetPandora(), LArClusterHelper::GetClusterHitType(pCluster))};
251  const float transverseClusterMinLengthAdjusted{ratio * m_transverseClusterMinLength};
252 
253  ClusterVector associatedClusters;
254 
255  this->GetAssociatedClusters(nearbyClusters, pCluster, inputAssociationMap, associatedClusters);
256 
257  if (this->GetTransverseSpan(pCluster, associatedClusters) < transverseClusterMinLengthAdjusted)
258  continue;
259 
260  transverseClusterList.push_back(new LArTransverseCluster(pCluster, associatedClusters));
261  }
262 }
263 
264 //------------------------------------------------------------------------------------------------------------------------------------------
265 
267  const TransverseClusterList &transverseClusterList, const ClusterAssociationMap &transverseAssociationMap,
268  ClusterAssociationMap &clusterAssociationMap) const
269 {
270  for (TransverseClusterList::const_iterator iter1 = transverseClusterList.begin(), iterEnd1 = transverseClusterList.end(); iter1 != iterEnd1; ++iter1)
271  {
272  LArTransverseCluster *const pInnerTransverseCluster = *iter1;
273  const Cluster *const pInnerCluster(pInnerTransverseCluster->GetSeedCluster());
274 
275  ClusterAssociationMap::const_iterator iterInner = transverseAssociationMap.find(pInnerCluster);
276  if (transverseAssociationMap.end() == iterInner)
277  continue;
278 
279  for (TransverseClusterList::const_iterator iter2 = transverseClusterList.begin(), iterEnd2 = transverseClusterList.end();
280  iter2 != iterEnd2; ++iter2)
281  {
282  LArTransverseCluster *const pOuterTransverseCluster = *iter2;
283  const Cluster *const pOuterCluster(pOuterTransverseCluster->GetSeedCluster());
284 
285  ClusterAssociationMap::const_iterator iterOuter = transverseAssociationMap.find(pOuterCluster);
286  if (transverseAssociationMap.end() == iterOuter)
287  continue;
288 
289  if (pInnerCluster == pOuterCluster)
290  continue;
291 
292  if (iterInner->second.m_forwardAssociations.count(pOuterCluster) == 0 || iterOuter->second.m_backwardAssociations.count(pInnerCluster) == 0)
293  continue;
294 
295  if (!this->IsExtremalCluster(true, pInnerCluster, pOuterCluster) || !this->IsExtremalCluster(false, pOuterCluster, pInnerCluster))
296  continue;
297 
298  if (!this->IsTransverseAssociated(pInnerTransverseCluster, pOuterTransverseCluster, nearbyClusters))
299  continue;
300 
301  clusterAssociationMap[pInnerCluster].m_forwardAssociations.insert(pOuterCluster);
302  clusterAssociationMap[pOuterCluster].m_backwardAssociations.insert(pInnerCluster);
303  }
304  }
305 }
306 
307 //------------------------------------------------------------------------------------------------------------------------------------------
308 
309 void TransverseAssociationAlgorithm::GetAssociatedClusters(const ClusterToClustersMap &nearbyClusters, const Cluster *const pClusterI,
310  const ClusterAssociationMap &associationMap, ClusterVector &associatedVector) const
311 {
312  ClusterAssociationMap::const_iterator iterI = associationMap.find(pClusterI);
313  if (associationMap.end() == iterI)
314  return;
315 
316  for (ClusterSet::const_iterator iterJ = iterI->second.m_forwardAssociations.begin(), iterEndJ = iterI->second.m_forwardAssociations.end();
317  iterJ != iterEndJ; ++iterJ)
318  {
319  const Cluster *const pClusterJ = *iterJ;
320 
321  if (this->IsTransverseAssociated(pClusterI, pClusterJ, nearbyClusters))
322  associatedVector.push_back(pClusterJ);
323  }
324 
325  for (ClusterSet::const_iterator iterJ = iterI->second.m_backwardAssociations.begin(), iterEndJ = iterI->second.m_backwardAssociations.end();
326  iterJ != iterEndJ; ++iterJ)
327  {
328  const Cluster *const pClusterJ = *iterJ;
329 
330  if (this->IsTransverseAssociated(pClusterJ, pClusterI, nearbyClusters))
331  associatedVector.push_back(pClusterJ);
332  }
333 
334  std::sort(associatedVector.begin(), associatedVector.end(), LArClusterHelper::SortByNHits);
335 }
336 
337 //------------------------------------------------------------------------------------------------------------------------------------------
338 
339 bool TransverseAssociationAlgorithm::IsAssociated(const bool isForward, const Cluster *const pFirstCluster,
340  const Cluster *const pSecondCluster, const ClusterToClustersMap &nearbyClusters) const
341 {
342  if ((0 == nearbyClusters.at(pFirstCluster).count(pSecondCluster)) || (0 == nearbyClusters.at(pSecondCluster).count(pFirstCluster)))
343  return false;
344 
345  CartesianVector firstInner(0.f, 0.f, 0.f), firstOuter(0.f, 0.f, 0.f);
346  CartesianVector secondInner(0.f, 0.f, 0.f), secondOuter(0.f, 0.f, 0.f);
347  this->GetExtremalCoordinatesX(pFirstCluster, firstInner, firstOuter);
348  this->GetExtremalCoordinatesX(pSecondCluster, secondInner, secondOuter);
349 
350  const CartesianVector firstCoordinate(isForward ? firstOuter : firstInner);
351  const CartesianVector secondCoordinate(isForward ? secondOuter : secondInner);
352 
353  if ((firstCoordinate.GetZ() > std::max(secondInner.GetZ(), secondOuter.GetZ()) + m_maxLongitudinalOverlap) ||
354  (firstCoordinate.GetZ() < std::min(secondInner.GetZ(), secondOuter.GetZ()) - m_maxLongitudinalOverlap))
355  return false;
356 
357  if ((isForward && secondCoordinate.GetX() < firstCoordinate.GetX()) || (!isForward && secondCoordinate.GetX() > firstCoordinate.GetX()))
358  return false;
359 
360  const CartesianVector firstProjection(LArClusterHelper::GetClosestPosition(firstCoordinate, pSecondCluster));
361 
362  if ((isForward && firstProjection.GetX() < firstCoordinate.GetX() - m_maxTransverseOverlap) ||
363  (!isForward && firstProjection.GetX() > firstCoordinate.GetX() + m_maxTransverseOverlap))
364  return false;
365 
366  if ((isForward && firstProjection.GetX() > firstCoordinate.GetX() + m_clusterWindow) ||
367  (!isForward && firstProjection.GetX() < firstCoordinate.GetX() - m_clusterWindow))
368  return false;
369 
370  return true;
371 }
372 
373 //------------------------------------------------------------------------------------------------------------------------------------------
374 
376  const Cluster *const pInnerCluster, const Cluster *const pOuterCluster, const ClusterToClustersMap &nearbyClusters) const
377 {
378  if ((0 == nearbyClusters.at(pInnerCluster).count(pOuterCluster)) || (0 == nearbyClusters.at(pOuterCluster).count(pInnerCluster)))
379  return false;
380 
381  CartesianVector innerInner(0.f, 0.f, 0.f), innerOuter(0.f, 0.f, 0.f);
382  CartesianVector outerInner(0.f, 0.f, 0.f), outerOuter(0.f, 0.f, 0.f);
383  this->GetExtremalCoordinatesX(pInnerCluster, innerInner, innerOuter);
384  this->GetExtremalCoordinatesX(pOuterCluster, outerInner, outerOuter);
385 
386  const CartesianVector innerCentroid((innerInner + innerOuter) * 0.5);
387  const CartesianVector outerCentroid((outerInner + outerOuter) * 0.5);
388 
389  if ((std::fabs(innerCentroid.GetZ() - outerInner.GetZ()) > std::fabs(innerCentroid.GetX() - outerInner.GetX()) * std::fabs(m_clusterTanAngle)) &&
390  (std::fabs(innerCentroid.GetZ() - outerOuter.GetZ()) > std::fabs(innerCentroid.GetX() - outerOuter.GetX()) * std::fabs(m_clusterTanAngle)))
391  return false;
392 
393  if ((std::fabs(outerCentroid.GetZ() - innerInner.GetZ()) > std::fabs(outerCentroid.GetX() - innerInner.GetX()) * std::fabs(m_clusterTanAngle)) &&
394  (std::fabs(outerCentroid.GetZ() - innerOuter.GetZ()) > std::fabs(outerCentroid.GetX() - innerOuter.GetX()) * std::fabs(m_clusterTanAngle)))
395  return false;
396 
397  const CartesianVector innerProjection(LArClusterHelper::GetClosestPosition(outerInner, pInnerCluster));
398  const CartesianVector outerProjection(LArClusterHelper::GetClosestPosition(innerOuter, pOuterCluster));
399 
400  if (innerOuter.GetX() > innerProjection.GetX() + m_maxTransverseOverlap || outerInner.GetX() < outerProjection.GetX() - m_maxTransverseOverlap)
401  return false;
402 
403  return true;
404 }
405 
406 //------------------------------------------------------------------------------------------------------------------------------------------
407 
409  const LArTransverseCluster *const pOuterTransverseCluster, const ClusterToClustersMap &nearbyClusters) const
410 {
411  if (pInnerTransverseCluster->GetDirection().GetDotProduct(pOuterTransverseCluster->GetDirection()) < m_transverseClusterMinCosTheta)
412  return false;
413 
414  if (!this->IsTransverseAssociated(pInnerTransverseCluster, pOuterTransverseCluster->GetInnerVertex()))
415  return false;
416 
417  if (!this->IsTransverseAssociated(pOuterTransverseCluster, pInnerTransverseCluster->GetOuterVertex()))
418  return false;
419 
420  if (!this->IsTransverseAssociated(pInnerTransverseCluster->GetSeedCluster(), pOuterTransverseCluster->GetSeedCluster(), nearbyClusters))
421  return false;
422 
423  if (this->IsOverlapping(pInnerTransverseCluster->GetSeedCluster(), pOuterTransverseCluster->GetSeedCluster()))
424  return false;
425 
426  return true;
427 }
428 
429 //------------------------------------------------------------------------------------------------------------------------------------------
430 
431 bool TransverseAssociationAlgorithm::IsTransverseAssociated(const LArTransverseCluster *const pTransverseCluster, const CartesianVector &testVertex) const
432 {
433  const CartesianVector &innerVertex(pTransverseCluster->GetInnerVertex());
434  const CartesianVector &outerVertex(pTransverseCluster->GetOuterVertex());
435  const CartesianVector &direction(pTransverseCluster->GetDirection());
436 
437  const HitType view{LArClusterHelper::GetClusterHitType(pTransverseCluster->GetSeedCluster())};
438  const float ratio{LArGeometryHelper::GetWirePitchRatio(this->GetPandora(), view)};
439  const float transverseMaxDisplacementAdjusted{ratio * m_transverseClusterMaxDisplacement};
440  const float transverseMaxDisplacementSquaredAdjusted{transverseMaxDisplacementAdjusted * transverseMaxDisplacementAdjusted};
441 
442  if (direction.GetCrossProduct(testVertex - innerVertex).GetMagnitudeSquared() > transverseMaxDisplacementSquaredAdjusted)
443  return false;
444 
445  if ((direction.GetDotProduct(testVertex - innerVertex) < -1.f * m_clusterWindow) ||
446  (direction.GetDotProduct(testVertex - outerVertex) > +1.f * m_clusterWindow))
447  return false;
448 
449  return true;
450 }
451 
452 //------------------------------------------------------------------------------------------------------------------------------------------
453 
454 bool TransverseAssociationAlgorithm::IsOverlapping(const Cluster *const pInnerCluster, const Cluster *const pOuterCluster) const
455 {
456  CartesianVector innerInner(0.f, 0.f, 0.f), innerOuter(0.f, 0.f, 0.f);
457  CartesianVector outerInner(0.f, 0.f, 0.f), outerOuter(0.f, 0.f, 0.f);
458  this->GetExtremalCoordinatesX(pInnerCluster, innerInner, innerOuter);
459  this->GetExtremalCoordinatesX(pOuterCluster, outerInner, outerOuter);
460 
461  const CartesianVector innerProjection(LArClusterHelper::GetClosestPosition(outerInner, pInnerCluster));
462  const CartesianVector outerProjection(LArClusterHelper::GetClosestPosition(innerOuter, pOuterCluster));
463 
464  const float innerOverlapSquared((innerProjection - innerOuter).GetMagnitudeSquared());
465  const float outerOverlapSquared((outerProjection - outerInner).GetMagnitudeSquared());
466 
467  return (std::max(innerOverlapSquared, outerOverlapSquared) > m_maxProjectedOverlap * m_maxProjectedOverlap);
468 }
469 
470 //------------------------------------------------------------------------------------------------------------------------------------------
471 
472 float TransverseAssociationAlgorithm::GetTransverseSpan(const Cluster *const pCluster) const
473 {
474  float minX(+std::numeric_limits<float>::max());
475  float maxX(-std::numeric_limits<float>::max());
476 
477  this->GetExtremalCoordinatesX(pCluster, minX, maxX);
478 
479  return (maxX - minX);
480 }
481 
482 //------------------------------------------------------------------------------------------------------------------------------------------
483 
484 float TransverseAssociationAlgorithm::GetLongitudinalSpan(const Cluster *const pCluster) const
485 {
486  float minZ(+std::numeric_limits<float>::max());
487  float maxZ(-std::numeric_limits<float>::max());
488 
489  this->GetExtremalCoordinatesZ(pCluster, minZ, maxZ);
490 
491  return (maxZ - minZ);
492 }
493 
494 //------------------------------------------------------------------------------------------------------------------------------------------
495 
496 float TransverseAssociationAlgorithm::GetTransverseSpan(const Cluster *const pCentralCluster, const ClusterVector &associatedClusters) const
497 {
498  float overallMinX(+std::numeric_limits<float>::max());
499  float overallMaxX(-std::numeric_limits<float>::max());
500 
501  this->GetExtremalCoordinatesX(pCentralCluster, overallMinX, overallMaxX);
502 
503  float localMinX(+std::numeric_limits<float>::max());
504  float localMaxX(-std::numeric_limits<float>::max());
505 
506  for (ClusterVector::const_iterator iter = associatedClusters.begin(), iterEnd = associatedClusters.end(); iter != iterEnd; ++iter)
507  {
508  const Cluster *const pAssociatedCluster = *iter;
509 
510  this->GetExtremalCoordinatesX(pAssociatedCluster, localMinX, localMaxX);
511 
512  if (localMinX < overallMinX)
513  overallMinX = localMinX;
514 
515  if (localMaxX > overallMaxX)
516  overallMaxX = localMaxX;
517  }
518 
519  return (overallMaxX - overallMinX);
520 }
521 
522 //------------------------------------------------------------------------------------------------------------------------------------------
523 
524 bool TransverseAssociationAlgorithm::IsExtremalCluster(const bool isForward, const Cluster *const pCurrentCluster, const Cluster *const pTestCluster) const
525 {
526  float currentMinX(0.f), currentMaxX(0.f);
527  this->GetExtremalCoordinatesX(pCurrentCluster, currentMinX, currentMaxX);
528 
529  float testMinX(0.f), testMaxX(0.f);
530  this->GetExtremalCoordinatesX(pTestCluster, testMinX, testMaxX);
531 
532  if (isForward)
533  {
534  if (std::fabs(testMaxX - currentMaxX) > std::numeric_limits<float>::epsilon())
535  return (testMaxX > currentMaxX);
536  }
537  else
538  {
539  if (std::fabs(testMinX - currentMaxX) > std::numeric_limits<float>::epsilon())
540  return (testMinX < currentMinX);
541  }
542 
543  return LArClusterHelper::SortByNHits(pTestCluster, pCurrentCluster);
544 }
545 
546 //------------------------------------------------------------------------------------------------------------------------------------------
547 
548 void TransverseAssociationAlgorithm::GetExtremalCoordinatesX(const Cluster *const pCluster, float &minX, float &maxX) const
549 {
550  return this->GetExtremalCoordinatesXZ(pCluster, true, minX, maxX);
551 }
552 
553 //------------------------------------------------------------------------------------------------------------------------------------------
554 
555 void TransverseAssociationAlgorithm::GetExtremalCoordinatesZ(const Cluster *const pCluster, float &minZ, float &maxZ) const
556 {
557  return this->GetExtremalCoordinatesXZ(pCluster, false, minZ, maxZ);
558 }
559 
560 //------------------------------------------------------------------------------------------------------------------------------------------
561 
562 void TransverseAssociationAlgorithm::GetExtremalCoordinatesXZ(const Cluster *const pCluster, const bool useX, float &minXZ, float &maxXZ) const
563 {
564  minXZ = +std::numeric_limits<float>::max();
565  maxXZ = -std::numeric_limits<float>::max();
566 
567  const OrderedCaloHitList &orderedCaloHitList(pCluster->GetOrderedCaloHitList());
568 
569  for (OrderedCaloHitList::const_iterator iter = orderedCaloHitList.begin(), iterEnd = orderedCaloHitList.end(); iter != iterEnd; ++iter)
570  {
571  for (CaloHitList::const_iterator hitIter = iter->second->begin(), hitIterEnd = iter->second->end(); hitIter != hitIterEnd; ++hitIter)
572  {
573  const float caloHitXZ(useX ? (*hitIter)->GetPositionVector().GetX() : (*hitIter)->GetPositionVector().GetZ());
574 
575  if (caloHitXZ < minXZ)
576  minXZ = caloHitXZ;
577 
578  if (caloHitXZ > maxXZ)
579  maxXZ = caloHitXZ;
580  }
581  }
582 
583  if (maxXZ < minXZ)
584  throw pandora::StatusCodeException(STATUS_CODE_FAILURE);
585 }
586 
587 //------------------------------------------------------------------------------------------------------------------------------------------
588 
590  const Cluster *const pCluster, CartesianVector &innerCoordinate, CartesianVector &outerCoordinate) const
591 {
592  CartesianVector firstCoordinate(0.f, 0.f, 0.f), secondCoordinate(0.f, 0.f, 0.f);
593  LArClusterHelper::GetExtremalCoordinates(pCluster, firstCoordinate, secondCoordinate);
594 
595  innerCoordinate = (firstCoordinate.GetX() < secondCoordinate.GetX() ? firstCoordinate : secondCoordinate);
596  outerCoordinate = (firstCoordinate.GetX() > secondCoordinate.GetX() ? firstCoordinate : secondCoordinate);
597 }
598 
599 //------------------------------------------------------------------------------------------------------------------------------------------
600 
602  const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
603 {
604  return this->FillReducedAssociationMap(inputAssociationMap, inputAssociationMap, inputAssociationMap, outputAssociationMap);
605 }
606 
607 //------------------------------------------------------------------------------------------------------------------------------------------
608 
610  const ClusterAssociationMap &secondAssociationMap, const ClusterAssociationMap &secondAssociationMapSwapped,
611  ClusterAssociationMap &clusterAssociationMap) const
612 {
613  // Remove associations A->B from the first association map
614  // if A->C exists in the second map and C->B exists in the reversed second map
615 
616  // Method can also be accessed through FillReducedAssociationMap(input,output) method,
617  // which will remove association A->B from the input map if an association A->C and C->B
618  // already exists in the map.
619 
620  ClusterVector sortedClusters;
621  for (const auto &mapEntry : firstAssociationMap)
622  sortedClusters.push_back(mapEntry.first);
623  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
624 
625  for (const Cluster *const pCluster : sortedClusters)
626  {
627  const ClusterAssociation &firstAssociation(firstAssociationMap.at(pCluster));
628 
629  ClusterVector sortedOuterClusters(firstAssociation.m_forwardAssociations.begin(), firstAssociation.m_forwardAssociations.end());
630  std::sort(sortedOuterClusters.begin(), sortedOuterClusters.end(), LArClusterHelper::SortByNHits);
631 
632  ClusterVector sortedInnerClusters(firstAssociation.m_backwardAssociations.begin(), firstAssociation.m_backwardAssociations.end());
633  std::sort(sortedInnerClusters.begin(), sortedInnerClusters.end(), LArClusterHelper::SortByNHits);
634 
635  ClusterAssociationMap::const_iterator iterSecond = secondAssociationMap.find(pCluster);
636  ClusterVector sortedMiddleClustersF, sortedMiddleClustersB;
637 
638  if (secondAssociationMap.end() != iterSecond)
639  {
640  sortedMiddleClustersF.insert(sortedMiddleClustersF.end(), iterSecond->second.m_forwardAssociations.begin(),
641  iterSecond->second.m_forwardAssociations.end());
642  sortedMiddleClustersB.insert(sortedMiddleClustersB.end(), iterSecond->second.m_backwardAssociations.begin(),
643  iterSecond->second.m_backwardAssociations.end());
644  std::sort(sortedMiddleClustersF.begin(), sortedMiddleClustersF.end(), LArClusterHelper::SortByNHits);
645  std::sort(sortedMiddleClustersB.begin(), sortedMiddleClustersB.end(), LArClusterHelper::SortByNHits);
646  }
647 
648  for (const Cluster *const pOuterCluster : sortedOuterClusters)
649  {
650  bool isNeighbouringCluster(true);
651 
652  for (const Cluster *const pMiddleCluster : sortedMiddleClustersF)
653  {
654  ClusterAssociationMap::const_iterator iterSecondCheck = secondAssociationMapSwapped.find(pMiddleCluster);
655  if (secondAssociationMapSwapped.end() == iterSecondCheck)
656  continue;
657 
658  if (iterSecondCheck->second.m_forwardAssociations.count(pOuterCluster) > 0)
659  {
660  isNeighbouringCluster = false;
661  break;
662  }
663  }
664 
665  if (isNeighbouringCluster)
666  clusterAssociationMap[pCluster].m_forwardAssociations.insert(pOuterCluster);
667  }
668 
669  for (const Cluster *const pInnerCluster : sortedInnerClusters)
670  {
671  bool isNeighbouringCluster(true);
672 
673  for (const Cluster *const pMiddleCluster : sortedMiddleClustersB)
674  {
675  ClusterAssociationMap::const_iterator iterSecondCheck = secondAssociationMapSwapped.find(pMiddleCluster);
676  if (secondAssociationMapSwapped.end() == iterSecondCheck)
677  continue;
678 
679  if (iterSecondCheck->second.m_backwardAssociations.count(pInnerCluster) > 0)
680  {
681  isNeighbouringCluster = false;
682  break;
683  }
684  }
685 
686  if (isNeighbouringCluster)
687  clusterAssociationMap[pCluster].m_backwardAssociations.insert(pInnerCluster);
688  }
689  }
690 }
691 
692 //------------------------------------------------------------------------------------------------------------------------------------------
693 
695  const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
696 {
697  // Generate a symmetrised association map, so that both A--Fwd-->B and B--Bwd-->A both exist.
698  // If A is associated to B through both a backward and forward association (very bad!),
699  // try to rationalise this through majority voting, otherwise remove the association.
700 
701  ClusterVector sortedClusters;
702  for (const auto &mapEntry : inputAssociationMap)
703  sortedClusters.push_back(mapEntry.first);
704  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
705 
706  for (const Cluster *const pCluster : sortedClusters)
707  {
708  const ClusterAssociation &inputAssociation(inputAssociationMap.at(pCluster));
709 
710  ClusterVector sortedForwardClusters(inputAssociation.m_forwardAssociations.begin(), inputAssociation.m_forwardAssociations.end());
711  std::sort(sortedForwardClusters.begin(), sortedForwardClusters.end(), LArClusterHelper::SortByNHits);
712 
713  ClusterVector sortedBackwardClusters(inputAssociation.m_backwardAssociations.begin(), inputAssociation.m_backwardAssociations.end());
714  std::sort(sortedBackwardClusters.begin(), sortedBackwardClusters.end(), LArClusterHelper::SortByNHits);
715 
716  // Symmetrise forward associations
717  for (const Cluster *const pForwardCluster : sortedForwardClusters)
718  {
719  int nCounter(+1);
720 
721  if (inputAssociation.m_backwardAssociations.count(pForwardCluster))
722  --nCounter;
723 
724  ClusterAssociationMap::const_iterator iterCheck = inputAssociationMap.find(pForwardCluster);
725  if (inputAssociationMap.end() != iterCheck)
726  {
727  if (iterCheck->second.m_forwardAssociations.count(pCluster))
728  --nCounter;
729 
730  if (iterCheck->second.m_backwardAssociations.count(pCluster))
731  ++nCounter;
732  }
733 
734  if (nCounter > 0)
735  {
736  if (!(outputAssociationMap[pCluster].m_backwardAssociations.count(pForwardCluster) == 0 &&
737  outputAssociationMap[pForwardCluster].m_forwardAssociations.count(pCluster) == 0))
738  throw StatusCodeException(STATUS_CODE_FAILURE);
739 
740  outputAssociationMap[pCluster].m_forwardAssociations.insert(pForwardCluster);
741  outputAssociationMap[pForwardCluster].m_backwardAssociations.insert(pCluster);
742  }
743  }
744 
745  // Symmetrise backward associations
746  for (const Cluster *const pBackwardCluster : sortedBackwardClusters)
747  {
748  int nCounter(-1);
749 
750  if (inputAssociation.m_forwardAssociations.count(pBackwardCluster))
751  ++nCounter;
752 
753  ClusterAssociationMap::const_iterator iterCheck = inputAssociationMap.find(pBackwardCluster);
754  if (inputAssociationMap.end() != iterCheck)
755  {
756  if (iterCheck->second.m_backwardAssociations.count(pCluster))
757  ++nCounter;
758 
759  if (iterCheck->second.m_forwardAssociations.count(pCluster))
760  --nCounter;
761  }
762 
763  if (nCounter < 0)
764  {
765  if (!(outputAssociationMap[pCluster].m_forwardAssociations.count(pBackwardCluster) == 0 &&
766  outputAssociationMap[pBackwardCluster].m_backwardAssociations.count(pCluster) == 0))
767  throw StatusCodeException(STATUS_CODE_FAILURE);
768 
769  outputAssociationMap[pCluster].m_backwardAssociations.insert(pBackwardCluster);
770  outputAssociationMap[pBackwardCluster].m_forwardAssociations.insert(pCluster);
771  }
772  }
773  }
774 }
775 
776 //------------------------------------------------------------------------------------------------------------------------------------------
777 
779  const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
780 {
781  ClusterAssociationMap intermediateAssociationMap;
782  this->FillSymmetricAssociationMap(inputAssociationMap, intermediateAssociationMap);
783  this->FillReducedAssociationMap(intermediateAssociationMap, outputAssociationMap);
784 }
785 
786 //------------------------------------------------------------------------------------------------------------------------------------------
787 //------------------------------------------------------------------------------------------------------------------------------------------
788 
789 TransverseAssociationAlgorithm::LArTransverseCluster::LArTransverseCluster(const Cluster *const pSeedCluster, const ClusterVector &associatedClusters) :
790  m_pSeedCluster(pSeedCluster),
791  m_associatedClusters(associatedClusters),
792  m_innerVertex(0.f, 0.f, 0.f),
793  m_outerVertex(0.f, 0.f, 0.f),
794  m_direction(0.f, 0.f, 0.f)
795 {
796  double Swxx(0.), Swzx(0.), Swz(0.), Swx(0.), Sw(0.);
797  double minX(std::numeric_limits<double>::max());
798  double maxX(-std::numeric_limits<double>::max());
799 
800  ClusterList clusterList(1, pSeedCluster);
801  clusterList.insert(clusterList.end(), associatedClusters.begin(), associatedClusters.end());
802 
803  for (ClusterList::const_iterator iterI = clusterList.begin(), iterEndI = clusterList.end(); iterI != iterEndI; ++iterI)
804  {
805  for (OrderedCaloHitList::const_iterator iterJ = (*iterI)->GetOrderedCaloHitList().begin(),
806  iterEndJ = (*iterI)->GetOrderedCaloHitList().end();
807  iterJ != iterEndJ; ++iterJ)
808  {
809  for (CaloHitList::const_iterator iterK = iterJ->second->begin(), iterEndK = iterJ->second->end(); iterK != iterEndK; ++iterK)
810  {
811  const CaloHit *const pCaloHit = *iterK;
812 
813  if (pCaloHit->GetPositionVector().GetX() < minX)
814  minX = pCaloHit->GetPositionVector().GetX();
815 
816  if (pCaloHit->GetPositionVector().GetX() > maxX)
817  maxX = pCaloHit->GetPositionVector().GetX();
818 
819  Swxx += pCaloHit->GetPositionVector().GetX() * pCaloHit->GetPositionVector().GetX();
820  Swzx += pCaloHit->GetPositionVector().GetZ() * pCaloHit->GetPositionVector().GetX();
821  Swz += pCaloHit->GetPositionVector().GetZ();
822  Swx += pCaloHit->GetPositionVector().GetX();
823  Sw += 1.;
824  }
825  }
826  }
827 
828  if (Sw > 0.f)
829  {
830  const double averageX(Swx / Sw);
831  const double averageZ(Swz / Sw);
832 
833  if (Sw * Swxx - Swx * Swx > 0.)
834  {
835  double m((Sw * Swzx - Swx * Swz) / (Sw * Swxx - Swx * Swx));
836  double px(1. / std::sqrt(1. + m * m));
837  double pz(m / std::sqrt(1. + m * m));
838 
839  m_innerVertex.SetValues(static_cast<float>(minX), 0.f, static_cast<float>(averageZ + m * (minX - averageX)));
840  m_outerVertex.SetValues(static_cast<float>(maxX), 0.f, static_cast<float>(averageZ + m * (maxX - averageX)));
841  m_direction.SetValues(static_cast<float>(px), 0.f, static_cast<float>(pz));
842  }
843  else
844  {
845  m_innerVertex.SetValues(static_cast<float>(averageX), 0.f, static_cast<float>(averageZ));
846  m_outerVertex.SetValues(static_cast<float>(averageX), 0.f, static_cast<float>(averageZ));
847  m_direction.SetValues(1.f, 0.f, 0.f);
848  }
849  }
850  else
851  {
852  throw StatusCodeException(STATUS_CODE_NOT_INITIALIZED);
853  }
854 }
855 
856 //------------------------------------------------------------------------------------------------------------------------------------------
857 //------------------------------------------------------------------------------------------------------------------------------------------
858 
859 StatusCode TransverseAssociationAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
860 {
861  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "FirstLengthCut", m_firstLengthCut));
862 
863  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SecondLengthCut", m_secondLengthCut));
864 
865  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ClusterWindow", m_clusterWindow));
866 
867  const StatusCode angleStatusCode(XmlHelper::ReadValue(xmlHandle, "clusterAngle", m_clusterAngle));
868 
869  if (STATUS_CODE_SUCCESS == angleStatusCode)
870  {
871  m_clusterCosAngle = std::cos(m_clusterAngle * M_PI / 180.f);
872  m_clusterTanAngle = std::tan(m_clusterAngle * M_PI / 180.f);
873  }
874  else if (STATUS_CODE_NOT_FOUND != angleStatusCode)
875  {
876  return angleStatusCode;
877  }
878 
879  PANDORA_RETURN_RESULT_IF_AND_IF(
880  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxTransverseOverlap", m_maxTransverseOverlap));
881 
882  PANDORA_RETURN_RESULT_IF_AND_IF(
883  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxProjectedOverlap", m_maxProjectedOverlap));
884 
885  PANDORA_RETURN_RESULT_IF_AND_IF(
886  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxLongitudinalOverlap", m_maxLongitudinalOverlap));
887 
888  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
889  XmlHelper::ReadValue(xmlHandle, "TransverseClusterMinCosTheta", m_transverseClusterMinCosTheta));
890 
891  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
892  XmlHelper::ReadValue(xmlHandle, "TransverseClusterMinLength", m_transverseClusterMinLength));
893 
894  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
895  XmlHelper::ReadValue(xmlHandle, "TransverseClusterMaxDisplacement", m_transverseClusterMaxDisplacement));
896 
898 }
899 
900 } // namespace lar_content
static bool SortByNHits(const pandora::Cluster *const pLhs, const pandora::Cluster *const pRhs)
Sort clusters by number of hits, then layer span, then inner layer, then position, then pulse-height.
void PopulateClusterAssociationMap(const pandora::ClusterVector &clusterVector, ClusterAssociationMap &clusterAssociationMap) const
Populate the cluster association map.
Header file for the kd tree linker algo template class.
static float GetWirePitchRatio(const pandora::Pandora &pandora, const pandora::HitType view)
Return the ratio of the wire pitch of the specified view to the minimum wire pitch for the detector...
const pandora::CartesianVector & GetInnerVertex() const
Get the inner vertex position.
float GetTransverseSpan(const pandora::Cluster *const pCluster) const
Calculate the overall span in X for a clusters.
void FillTransverseAssociationMap(const ClusterToClustersMap &nearbyClusters, const TransverseClusterList &transverseClusterList, const ClusterAssociationMap &transverseAssociationMap, ClusterAssociationMap &clusterAssociationMap) const
Form associations between transverse cluster objects.
Box structure used to define 2D field. It&#39;s used in KDTree building step to divide the detector space...
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
void GetExtremalCoordinatesXZ(const pandora::Cluster *const pCluster, const bool useX, float &minXZ, float &maxXZ) const
Get minimum and maximum X or Z coordinates for a given cluster.
std::unordered_map< const pandora::Cluster *, ClusterAssociation > ClusterAssociationMap
STL namespace.
intermediate_table::const_iterator const_iterator
bool IsExtremalCluster(const bool isForward, const pandora::Cluster *const pCurrentCluster, const pandora::Cluster *const pTestCluster) const
Determine which of two clusters is extremal.
void FillSymmetricAssociationMap(const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
Symmetrise an association map.
bool IsTransverseAssociated(const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2, const ClusterToClustersMap &nearbyClusters) const
Determine whether two clusters are within the same cluster window.
void FillAssociationMap(const ClusterToClustersMap &nearbyClusters, const pandora::ClusterVector &firstVector, const pandora::ClusterVector &secondVector, ClusterAssociationMap &firstAssociationMap, ClusterAssociationMap &secondAssociationMap) const
Form associations between two input lists of cluster.
static pandora::HitType GetClusterHitType(const pandora::Cluster *const pCluster)
Get the hit type associated with a two dimensional cluster.
std::vector< LArTransverseCluster * > TransverseClusterList
TFile f
Definition: plotHisto.C:6
bool IsOverlapping(const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2) const
Determine whether two clusters are overlapping.
Header file for the geometry helper class.
void GetExtremalCoordinatesX(const pandora::Cluster *const pCluster, float &minX, float &maxX) const
Get minimum and maximum X coordinates for a given cluster.
Header file for the cluster helper class.
float m_searchRegionX
Search region, applied to x dimension, for look-up from kd-trees.
void SortInputClusters(const pandora::ClusterVector &inputClusters, pandora::ClusterVector &shortClusters, pandora::ClusterVector &transverseMediumClusters, pandora::ClusterVector &longitudinalMediumClusters, pandora::ClusterVector &longClusters) const
Separate input clusters by length.
void build(std::vector< KDTreeNodeInfoT< DATA, DIM >> &eltList, const KDTreeBoxT< DIM > &region)
Build the KD tree from the "eltList" in the space define by "region".
void FillTransverseClusterList(const ClusterToClustersMap &nearbyClusters, const pandora::ClusterVector &inputClusters, const ClusterAssociationMap &inputAssociationMap, TransverseClusterList &transverseClusterList) const
Create transverse cluster objects, these are protoclusters with a direction and inner/outer vertices...
const pandora::CartesianVector & GetOuterVertex() const
Get the outer vertex position.
std::unordered_map< const pandora::Cluster *, pandora::ClusterSet > ClusterToClustersMap
std::unordered_map< const pandora::CaloHit *, const pandora::Cluster * > HitToClusterMap
Detector simulation of raw signals on wires.
static void GetExtremalCoordinates(const pandora::ClusterList &clusterList, pandora::CartesianVector &innerCoordinate, pandora::CartesianVector &outerCoordinate)
Get positions of the two most distant calo hits in a list of cluster (ordered by Z) ...
void FinalizeClusterAssociationMap(const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
Symmetrise and then remove double-counting from an association map.
HitType
Definition: HitType.h:12
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
void GetListOfCleanClusters(const pandora::ClusterList *const pClusterList, pandora::ClusterVector &clusterVector) const
Populate cluster vector with subset of cluster list, containing clusters judged to be clean...
KDTreeBox fill_and_bound_2d_kd_tree(const MANAGED_CONTAINER< const T * > &points, std::vector< KDTreeNodeInfoT< const T *, 2 >> &nodes)
fill_and_bound_2d_kd_tree
void GetNearbyClusterMap(const pandora::ClusterVector &allClusters, ClusterToClustersMap &nearbyClusters) const
Use a kd-tree to obtain details of all nearby cluster combinations.
const pandora::CartesianVector & GetDirection() const
Get the direction.
bool IsAssociated(const bool isForward, const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2, const ClusterToClustersMap &nearbyClusters) const
Determine whether clusters are association.
std::vector< art::Ptr< recob::Cluster > > ClusterVector
void FillReducedAssociationMap(const ClusterToClustersMap &nearbyClusters, const pandora::ClusterVector &firstVector, const pandora::ClusterVector &secondVector, ClusterAssociationMap &clusterAssociationMap) const
Form a reduced set of associations between two input lists of clusters.
float GetLongitudinalSpan(const pandora::Cluster *const pCluster) const
Calculate the overall span in Z for a clusters.
Header file for the transverse association algorithm class.
void GetAssociatedClusters(const ClusterToClustersMap &nearbyClusters, const pandora::Cluster *const pCluster, const ClusterAssociationMap &inputAssociationMap, pandora::ClusterVector &associatedClusters) const
Find the clusters that are transversely associated with a target cluster.
static pandora::CartesianVector GetClosestPosition(const pandora::CartesianVector &position, const pandora::ClusterList &clusterList)
Get closest position in a list of clusters to a specified input position vector.
float m_searchRegionZ
Search region, applied to u/v/w dimension, for look-up from kd-trees.
KDTreeBox build_2d_kd_search_region(const pandora::CaloHit *const point, const float x_span, const float z_span)
build_2d_kd_search_region
void search(const KDTreeBoxT< DIM > &searchBox, std::vector< KDTreeNodeInfoT< DATA, DIM >> &resRecHitList)
Search in the KDTree for all points that would be contained in the given searchbox The founded points...
void GetExtremalCoordinatesZ(const pandora::Cluster *const pCluster, float &minZ, float &maxZ) const
Get minimum and maximum Z coordinates for a given cluster.
LArTransverseCluster(const pandora::Cluster *const pSeedCluster, const pandora::ClusterVector &associatedClusters)
Constructor.