ThreeViewTrackFragmentsAlgorithm.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArThreeDReco/LArTrackFragments/ThreeViewTrackFragmentsAlgorithm.cc
3  *
4  * @brief Implementation of the three view fragments algorithm class.
5  *
6  * $Log: $
7  */
8 
9 #include "Pandora/AlgorithmHeaders.h"
10 
13 
15 
16 using namespace pandora;
17 
18 namespace lar_content
19 {
20 
21 ThreeViewTrackFragmentsAlgorithm::ThreeViewTrackFragmentsAlgorithm() :
22  m_nMaxTensorToolRepeats(1000),
23  m_minXOverlap(3.f),
24  m_minXOverlapFraction(0.8f),
25  m_maxPointDisplacementSquared(1.5f * 1.5f),
26  m_minMatchedSamplingPointFraction(0.5f),
27  m_minMatchedHits(5)
28 {
29 }
30 
31 //------------------------------------------------------------------------------------------------------------------------------------------
32 
33 void ThreeViewTrackFragmentsAlgorithm::UpdateForNewCluster(const Cluster *const pNewCluster)
34 {
35  try
36  {
37  this->AddToSlidingFitCache(pNewCluster);
38  }
39  catch (StatusCodeException &statusCodeException)
40  {
41  if (STATUS_CODE_FAILURE == statusCodeException.GetStatusCode())
42  throw statusCodeException;
43 
44  return;
45  }
46 
47  const HitType hitType(LArClusterHelper::GetClusterHitType(pNewCluster));
48 
49  if (!((TPC_VIEW_U == hitType) || (TPC_VIEW_V == hitType) || (TPC_VIEW_W == hitType)))
50  throw StatusCodeException(STATUS_CODE_FAILURE);
51 
52  // ATTN This is non-standard usage, supported here only (for legacy purposes)
53  MatchingType &matchingControl(this->GetMatchingControl());
54  ClusterList &clusterList((TPC_VIEW_U == hitType) ? matchingControl.m_clusterListU
55  : (TPC_VIEW_V == hitType) ? matchingControl.m_clusterListV : matchingControl.m_clusterListW);
56 
57  if (clusterList.end() != std::find(clusterList.begin(), clusterList.end(), pNewCluster))
58  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
59 
60  clusterList.push_back(pNewCluster);
61 
62  ClusterList clusterList1(this->GetSelectedClusterList((TPC_VIEW_U == hitType) ? TPC_VIEW_V : TPC_VIEW_U));
63  ClusterList clusterList2(this->GetSelectedClusterList((TPC_VIEW_W == hitType) ? TPC_VIEW_V : TPC_VIEW_W));
64  clusterList1.sort(LArClusterHelper::SortByNHits);
65  clusterList2.sort(LArClusterHelper::SortByNHits);
66 
67  for (const Cluster *const pCluster1 : clusterList1)
68  {
69  if (TPC_VIEW_U == hitType)
70  {
71  this->CalculateOverlapResult(pNewCluster, pCluster1, nullptr);
72  }
73  else if (TPC_VIEW_V == hitType)
74  {
75  this->CalculateOverlapResult(pCluster1, pNewCluster, nullptr);
76  }
77  else
78  {
79  this->CalculateOverlapResult(pCluster1, nullptr, pNewCluster);
80  }
81  }
82 
83  for (const Cluster *const pCluster2 : clusterList2)
84  {
85  if (TPC_VIEW_U == hitType)
86  {
87  this->CalculateOverlapResult(pNewCluster, nullptr, pCluster2);
88  }
89  else if (TPC_VIEW_V == hitType)
90  {
91  this->CalculateOverlapResult(nullptr, pNewCluster, pCluster2);
92  }
93  else
94  {
95  this->CalculateOverlapResult(nullptr, pCluster2, pNewCluster);
96  }
97  }
98 }
99 
100 //------------------------------------------------------------------------------------------------------------------------------------------
101 
102 void ThreeViewTrackFragmentsAlgorithm::RebuildClusters(const ClusterList &rebuildList, ClusterList &newClusters) const
103 {
104  const ClusterList *pNewClusterList = nullptr;
105  std::string oldClusterListName, newClusterListName;
106 
107  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::InitializeReclustering(*this, TrackList(), rebuildList, oldClusterListName));
108  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=,
109  PandoraContentApi::RunClusteringAlgorithm(*this, m_reclusteringAlgorithmName, pNewClusterList, newClusterListName));
110 
111  newClusters.insert(newClusters.end(), pNewClusterList->begin(), pNewClusterList->end());
112  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::EndReclustering(*this, newClusterListName));
113 }
114 
115 //------------------------------------------------------------------------------------------------------------------------------------------
116 
118 {
119  ClusterList clusterListU(this->GetSelectedClusterList(TPC_VIEW_U));
120  ClusterList clusterListV(this->GetSelectedClusterList(TPC_VIEW_V));
121  ClusterList clusterListW(this->GetSelectedClusterList(TPC_VIEW_W));
122  clusterListU.sort(LArClusterHelper::SortByNHits);
123  clusterListV.sort(LArClusterHelper::SortByNHits);
124  clusterListW.sort(LArClusterHelper::SortByNHits);
125 
126  for (const Cluster *const pClusterU : clusterListU)
127  {
128  for (const Cluster *const pClusterV : clusterListV)
129  this->CalculateOverlapResult(pClusterU, pClusterV, nullptr);
130  }
131 
132  for (const Cluster *const pClusterU : clusterListU)
133  {
134  for (const Cluster *const pClusterW : clusterListW)
135  this->CalculateOverlapResult(pClusterU, nullptr, pClusterW);
136  }
137 
138  for (const Cluster *const pClusterV : clusterListV)
139  {
140  for (const Cluster *const pClusterW : clusterListW)
141  this->CalculateOverlapResult(nullptr, pClusterV, pClusterW);
142  }
143 }
144 
145 //------------------------------------------------------------------------------------------------------------------------------------------
146 
147 void ThreeViewTrackFragmentsAlgorithm::CalculateOverlapResult(const Cluster *const pClusterU, const Cluster *const pClusterV, const Cluster *const pClusterW)
148 {
149  const HitType missingHitType(((nullptr != pClusterU) && (nullptr != pClusterV) && (nullptr == pClusterW))
150  ? TPC_VIEW_W
151  : ((nullptr != pClusterU) && (nullptr == pClusterV) && (nullptr != pClusterW))
152  ? TPC_VIEW_V
153  : ((nullptr == pClusterU) && (nullptr != pClusterV) && (nullptr != pClusterW)) ? TPC_VIEW_U : HIT_CUSTOM);
154 
155  if (HIT_CUSTOM == missingHitType)
156  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
157 
158  // Calculate new overlap result and replace old overlap result where necessary
159  FragmentOverlapResult oldOverlapResult, newOverlapResult;
160  const Cluster *pMatchedClusterU(nullptr), *pMatchedClusterV(nullptr), *pMatchedClusterW(nullptr);
161 
162  const TwoDSlidingFitResult &fitResult1(
163  (TPC_VIEW_U == missingHitType) ? this->GetCachedSlidingFitResult(pClusterV) : this->GetCachedSlidingFitResult(pClusterU));
164 
165  const TwoDSlidingFitResult &fitResult2(
166  (TPC_VIEW_U == missingHitType)
167  ? this->GetCachedSlidingFitResult(pClusterW)
168  : (TPC_VIEW_V == missingHitType) ? this->GetCachedSlidingFitResult(pClusterW) : this->GetCachedSlidingFitResult(pClusterV));
169 
170  const ClusterList &inputClusterList(this->GetInputClusterList(missingHitType));
171 
172  const Cluster *pBestMatchedCluster(nullptr);
173  const StatusCode statusCode(this->CalculateOverlapResult(fitResult1, fitResult2, inputClusterList, pBestMatchedCluster, newOverlapResult));
174 
175  if ((STATUS_CODE_SUCCESS != statusCode) && (STATUS_CODE_NOT_FOUND != statusCode))
176  throw StatusCodeException(statusCode);
177 
178  if (!newOverlapResult.IsInitialized())
179  return;
180 
181  MatchingType::TensorType &overlapTensor(this->GetMatchingControl().GetOverlapTensor());
182 
183  if (STATUS_CODE_SUCCESS == statusCode)
184  {
185  pMatchedClusterU = ((nullptr != pClusterU) ? pClusterU : pBestMatchedCluster);
186  pMatchedClusterV = ((nullptr != pClusterV) ? pClusterV : pBestMatchedCluster);
187  pMatchedClusterW = ((nullptr != pClusterW) ? pClusterW : pBestMatchedCluster);
188 
189  if (nullptr == pMatchedClusterU || nullptr == pMatchedClusterV || nullptr == pMatchedClusterW)
190  throw StatusCodeException(STATUS_CODE_FAILURE);
191 
192  try
193  {
194  oldOverlapResult = overlapTensor.GetOverlapResult(pMatchedClusterU, pMatchedClusterV, pMatchedClusterW);
195  }
196  catch (StatusCodeException &)
197  {
198  }
199  }
200 
201  if (!oldOverlapResult.IsInitialized())
202  {
203  overlapTensor.SetOverlapResult(pMatchedClusterU, pMatchedClusterV, pMatchedClusterW, newOverlapResult);
204  }
205  else if (newOverlapResult.GetFragmentCaloHitList().size() > oldOverlapResult.GetFragmentCaloHitList().size())
206  {
207  overlapTensor.ReplaceOverlapResult(pMatchedClusterU, pMatchedClusterV, pMatchedClusterW, newOverlapResult);
208  }
209  else if (newOverlapResult.GetFragmentCaloHitList().size() == oldOverlapResult.GetFragmentCaloHitList().size())
210  {
211  float newEnergySum(0.f), oldEnergySum(0.f);
212  for (const CaloHit *const pCaloHit : newOverlapResult.GetFragmentCaloHitList())
213  newEnergySum += pCaloHit->GetHadronicEnergy();
214  for (const CaloHit *const pCaloHit : oldOverlapResult.GetFragmentCaloHitList())
215  oldEnergySum += pCaloHit->GetHadronicEnergy();
216 
217  if (newEnergySum > oldEnergySum)
218  overlapTensor.ReplaceOverlapResult(pMatchedClusterU, pMatchedClusterV, pMatchedClusterW, newOverlapResult);
219  }
220 }
221 
222 //------------------------------------------------------------------------------------------------------------------------------------------
223 
225  const ClusterList &inputClusterList, const Cluster *&pBestMatchedCluster, FragmentOverlapResult &fragmentOverlapResult) const
226 {
227  const Cluster *const pCluster1(fitResult1.GetCluster());
228  const Cluster *const pCluster2(fitResult2.GetCluster());
229 
230  float xMin1(0.f), xMax1(0.f), xMin2(0.f), xMax2(0.f);
231  pCluster1->GetClusterSpanX(xMin1, xMax1);
232  pCluster2->GetClusterSpanX(xMin2, xMax2);
233 
234  const float xOverlap(std::min(xMax1, xMax2) - std::max(xMin1, xMin2));
235 
236  if (xOverlap < std::numeric_limits<float>::epsilon())
237  return STATUS_CODE_NOT_FOUND;
238 
239  CartesianPointVector projectedPositions;
240  const StatusCode statusCode1(this->GetProjectedPositions(fitResult1, fitResult2, projectedPositions));
241 
242  if (STATUS_CODE_SUCCESS != statusCode1)
243  return statusCode1;
244 
245  CaloHitList matchedHits;
246  ClusterList matchedClusters;
247  HitToClusterMap hitToClusterMap;
248  const StatusCode statusCode2(this->GetMatchedHits(inputClusterList, projectedPositions, hitToClusterMap, matchedHits));
249 
250  if (STATUS_CODE_SUCCESS != statusCode2)
251  return statusCode2;
252 
253  const StatusCode statusCode3(this->GetMatchedClusters(matchedHits, hitToClusterMap, matchedClusters, pBestMatchedCluster));
254 
255  if (STATUS_CODE_SUCCESS != statusCode3)
256  return statusCode3;
257 
258  if (!this->CheckMatchedClusters(projectedPositions, matchedClusters))
259  return STATUS_CODE_NOT_FOUND;
260 
261  FragmentOverlapResult overlapResult;
262  this->GetFragmentOverlapResult(projectedPositions, matchedHits, matchedClusters, overlapResult);
263 
264  if (!this->CheckOverlapResult(overlapResult))
265  return STATUS_CODE_NOT_FOUND;
266 
267  fragmentOverlapResult = overlapResult;
268  return STATUS_CODE_SUCCESS;
269 }
270 
271 //------------------------------------------------------------------------------------------------------------------------------------------
272 
274  const TwoDSlidingFitResult &fitResult1, const TwoDSlidingFitResult &fitResult2, CartesianPointVector &projectedPositions) const
275 {
276  const Cluster *const pCluster1(fitResult1.GetCluster());
277  const Cluster *const pCluster2(fitResult2.GetCluster());
278 
279  // Check hit types
280  const HitType hitType1(LArClusterHelper::GetClusterHitType(pCluster1));
281  const HitType hitType2(LArClusterHelper::GetClusterHitType(pCluster2));
282  const HitType hitType3((TPC_VIEW_U != hitType1 && TPC_VIEW_U != hitType2)
283  ? TPC_VIEW_U
284  : (TPC_VIEW_V != hitType1 && TPC_VIEW_V != hitType2)
285  ? TPC_VIEW_V
286  : (TPC_VIEW_W != hitType1 && TPC_VIEW_W != hitType2) ? TPC_VIEW_W : HIT_CUSTOM);
287 
288  if (HIT_CUSTOM == hitType3)
289  return STATUS_CODE_INVALID_PARAMETER;
290 
291  // Check absolute and fractional overlap in x coordinate
292  float xMin1(0.f), xMax1(0.f), xMin2(0.f), xMax2(0.f);
293  pCluster1->GetClusterSpanX(xMin1, xMax1);
294  pCluster2->GetClusterSpanX(xMin2, xMax2);
295 
296  const float xOverlap(std::min(xMax1, xMax2) - std::max(xMin1, xMin2));
297  const float xSpan(std::max(xMax1, xMax2) - std::min(xMin1, xMin2));
298 
299  if ((xOverlap < m_minXOverlap) || (xSpan < std::numeric_limits<float>::epsilon()) || ((xOverlap / xSpan) < m_minXOverlapFraction))
300  return STATUS_CODE_NOT_FOUND;
301 
302  // Identify vertex and end positions (2D)
303  const CartesianVector minPosition1(fitResult1.GetGlobalMinLayerPosition());
304  const CartesianVector maxPosition1(fitResult1.GetGlobalMaxLayerPosition());
305  const CartesianVector minPosition2(fitResult2.GetGlobalMinLayerPosition());
306  const CartesianVector maxPosition2(fitResult2.GetGlobalMaxLayerPosition());
307 
308  const float dx_A(std::fabs(minPosition2.GetX() - minPosition1.GetX()));
309  const float dx_B(std::fabs(maxPosition2.GetX() - maxPosition1.GetX()));
310  const float dx_C(std::fabs(maxPosition2.GetX() - minPosition1.GetX()));
311  const float dx_D(std::fabs(minPosition2.GetX() - maxPosition1.GetX()));
312 
313  if (std::min(dx_C, dx_D) > std::max(dx_A, dx_B) && std::min(dx_A, dx_B) > std::max(dx_C, dx_D))
314  return STATUS_CODE_NOT_FOUND;
315 
316  const CartesianVector &vtxPosition1(minPosition1);
317  const CartesianVector &endPosition1(maxPosition1);
318  const CartesianVector &vtxPosition2((dx_A < dx_C) ? minPosition2 : maxPosition2);
319  const CartesianVector &endPosition2((dx_A < dx_C) ? maxPosition2 : minPosition2);
320 
321  // Calculate vertex and end positions (3D)
322  float vtxChi2(0.f);
323  CartesianVector vtxPosition3D(0.f, 0.f, 0.f);
324  LArGeometryHelper::MergeTwoPositions3D(this->GetPandora(), hitType1, hitType2, vtxPosition1, vtxPosition2, vtxPosition3D, vtxChi2);
325 
326  float endChi2(0.f);
327  CartesianVector endPosition3D(0.f, 0.f, 0.f);
328  LArGeometryHelper::MergeTwoPositions3D(this->GetPandora(), hitType1, hitType2, endPosition1, endPosition2, endPosition3D, endChi2);
329 
330  const CartesianVector vtxProjection3(LArGeometryHelper::ProjectPosition(this->GetPandora(), vtxPosition3D, hitType3));
331  const CartesianVector endProjection3(LArGeometryHelper::ProjectPosition(this->GetPandora(), endPosition3D, hitType3));
332 
333  const float samplingPitch(0.5f * LArGeometryHelper::GetWireZPitch(this->GetPandora()));
334  const float nSamplingPoints((endProjection3 - vtxProjection3).GetMagnitude() / samplingPitch);
335 
336  if (nSamplingPoints < 1.f)
337  return STATUS_CODE_NOT_FOUND;
338 
339  // Loop over trajectory points
340  bool foundLastPosition(false);
341  CartesianVector lastPosition(0.f, 0.f, 0.f);
342 
343  for (float iSample = 0.5f; iSample < nSamplingPoints; iSample += 1.f)
344  {
345  const CartesianVector linearPosition3D(vtxPosition3D + (endPosition3D - vtxPosition3D) * (iSample / nSamplingPoints));
346  const CartesianVector linearPosition1(LArGeometryHelper::ProjectPosition(this->GetPandora(), linearPosition3D, hitType1));
347  const CartesianVector linearPosition2(LArGeometryHelper::ProjectPosition(this->GetPandora(), linearPosition3D, hitType2));
348 
349  float chi2(0.f);
350  CartesianVector fitPosition1(0.f, 0.f, 0.f), fitPosition2(0.f, 0.f, 0.f);
351 
352  if ((STATUS_CODE_SUCCESS != fitResult1.GetGlobalFitProjection(linearPosition1, fitPosition1)) ||
353  (STATUS_CODE_SUCCESS != fitResult2.GetGlobalFitProjection(linearPosition2, fitPosition2)))
354  {
355  continue;
356  }
357 
358  float rL1(0.f), rL2(0.f), rT1(0.f), rT2(0.f);
359  fitResult1.GetLocalPosition(fitPosition1, rL1, rT1);
360  fitResult2.GetLocalPosition(fitPosition2, rL2, rT2);
361 
362  const float x(0.5 * (fitPosition1.GetX() + fitPosition2.GetX()));
363  CartesianVector position1(0.f, 0.f, 0.f), position2(0.f, 0.f, 0.f), position3(0.f, 0.f, 0.f);
364 
365  try
366  {
367  const FitSegment &fitSegment1 = fitResult1.GetFitSegment(rL1);
368  const FitSegment &fitSegment2 = fitResult2.GetFitSegment(rL2);
369 
370  if ((STATUS_CODE_SUCCESS != fitResult1.GetTransverseProjection(x, fitSegment1, position1)) ||
371  (STATUS_CODE_SUCCESS != fitResult2.GetTransverseProjection(x, fitSegment2, position2)))
372  {
373  continue;
374  }
375  }
376  catch (StatusCodeException &statusCodeException)
377  {
378  if (STATUS_CODE_FAILURE == statusCodeException.GetStatusCode())
379  throw statusCodeException;
380 
381  continue;
382  }
383 
384  LArGeometryHelper::MergeTwoPositions(this->GetPandora(), hitType1, hitType2, position1, position2, position3, chi2);
385 
386  // TODO For highly multi-valued x, projected positions can be unreliable. Need to make interpolation more robust for these cases.
387  if (foundLastPosition)
388  {
389  const float thisDisplacement((lastPosition - position3).GetMagnitude());
390  if (thisDisplacement > 2.f * samplingPitch)
391  {
392  const float nExtraPoints(thisDisplacement / samplingPitch);
393  for (float iExtra = 0.5f; iExtra < nExtraPoints; iExtra += 1.f)
394  {
395  const CartesianVector extraPosition(position3 + (lastPosition - position3) * (iExtra / nExtraPoints));
396  projectedPositions.push_back(extraPosition);
397  }
398  }
399  }
400 
401  projectedPositions.push_back(position3);
402 
403  lastPosition = position3;
404  foundLastPosition = true;
405  }
406 
407  // Bail out if list of projected positions is empty
408  if (projectedPositions.empty())
409  return STATUS_CODE_NOT_FOUND;
410 
411  return STATUS_CODE_SUCCESS;
412 }
413 
414 //------------------------------------------------------------------------------------------------------------------------------------------
415 
416 StatusCode ThreeViewTrackFragmentsAlgorithm::GetMatchedHits(const ClusterList &inputClusterList,
417  const CartesianPointVector &projectedPositions, HitToClusterMap &hitToClusterMap, CaloHitList &matchedHits) const
418 {
419  CaloHitVector availableCaloHits;
420 
421  for (ClusterList::const_iterator iter = inputClusterList.begin(), iterEnd = inputClusterList.end(); iter != iterEnd; ++iter)
422  {
423  const Cluster *const pCluster = *iter;
424 
425  if (!pCluster->IsAvailable())
426  continue;
427 
428  CaloHitList caloHitList;
429  pCluster->GetOrderedCaloHitList().FillCaloHitList(caloHitList);
430  availableCaloHits.insert(availableCaloHits.end(), caloHitList.begin(), caloHitList.end());
431 
432  for (CaloHitList::const_iterator hIter = caloHitList.begin(), hIterEnd = caloHitList.end(); hIter != hIterEnd; ++hIter)
433  hitToClusterMap.insert(HitToClusterMap::value_type(*hIter, pCluster));
434  }
435 
436  std::sort(availableCaloHits.begin(), availableCaloHits.end(), LArClusterHelper::SortHitsByPosition);
437 
438  for (const CartesianVector &projectedPosition : projectedPositions)
439  {
440  const CaloHit *pClosestCaloHit(nullptr);
441  float closestDistanceSquared(std::numeric_limits<float>::max()), tieBreakerBestEnergy(0.f);
442 
443  for (const CaloHit *const pCaloHit : availableCaloHits)
444  {
445  const float distanceSquared((pCaloHit->GetPositionVector() - projectedPosition).GetMagnitudeSquared());
446 
447  if ((distanceSquared < closestDistanceSquared) ||
448  ((std::fabs(distanceSquared - closestDistanceSquared) < std::numeric_limits<float>::epsilon()) &&
449  (pCaloHit->GetHadronicEnergy() > tieBreakerBestEnergy)))
450  {
451  pClosestCaloHit = pCaloHit;
452  closestDistanceSquared = distanceSquared;
453  tieBreakerBestEnergy = pCaloHit->GetHadronicEnergy();
454  }
455  }
456 
457  if ((closestDistanceSquared < m_maxPointDisplacementSquared) && (nullptr != pClosestCaloHit) &&
458  (matchedHits.end() == std::find(matchedHits.begin(), matchedHits.end(), pClosestCaloHit)))
459  matchedHits.push_back(pClosestCaloHit);
460  }
461 
462  if (matchedHits.empty())
463  return STATUS_CODE_NOT_FOUND;
464 
465  return STATUS_CODE_SUCCESS;
466 }
467 
468 //------------------------------------------------------------------------------------------------------------------------------------------
469 
470 StatusCode ThreeViewTrackFragmentsAlgorithm::GetMatchedClusters(const CaloHitList &matchedHits, const HitToClusterMap &hitToClusterMap,
471  ClusterList &matchedClusters, const Cluster *&pBestMatchedCluster) const
472 {
473  ClusterToMatchedHitsMap clusterToMatchedHitsMap;
474 
475  for (CaloHitList::const_iterator iter = matchedHits.begin(), iterEnd = matchedHits.end(); iter != iterEnd; ++iter)
476  {
477  const CaloHit *const pCaloHit = *iter;
478  HitToClusterMap::const_iterator cIter = hitToClusterMap.find(pCaloHit);
479 
480  if (hitToClusterMap.end() == cIter)
481  throw StatusCodeException(STATUS_CODE_FAILURE);
482 
483  ++clusterToMatchedHitsMap[cIter->second];
484 
485  if (matchedClusters.end() == std::find(matchedClusters.begin(), matchedClusters.end(), cIter->second))
486  matchedClusters.push_back(cIter->second);
487  }
488 
489  if (matchedClusters.empty())
490  return STATUS_CODE_NOT_FOUND;
491 
492  pBestMatchedCluster = nullptr;
493  unsigned int bestClusterMatchedHits(0);
494  float tieBreakerBestEnergy(0.f);
495 
496  ClusterVector sortedClusters;
497  for (const auto &mapEntry : clusterToMatchedHitsMap)
498  sortedClusters.push_back(mapEntry.first);
499  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
500 
501  for (const Cluster *const pCluster : sortedClusters)
502  {
503  const unsigned int nMatchedHits(clusterToMatchedHitsMap.at(pCluster));
504 
505  if ((nMatchedHits > bestClusterMatchedHits) || ((nMatchedHits == bestClusterMatchedHits) && (pCluster->GetHadronicEnergy() > tieBreakerBestEnergy)))
506  {
507  pBestMatchedCluster = pCluster;
508  bestClusterMatchedHits = nMatchedHits;
509  tieBreakerBestEnergy = pCluster->GetHadronicEnergy();
510  }
511  }
512 
513  if (nullptr == pBestMatchedCluster)
514  return STATUS_CODE_NOT_FOUND;
515 
516  return STATUS_CODE_SUCCESS;
517 }
518 
519 //------------------------------------------------------------------------------------------------------------------------------------------
520 
521 void ThreeViewTrackFragmentsAlgorithm::GetFragmentOverlapResult(const CartesianPointVector &projectedPositions,
522  const CaloHitList &matchedHits, const ClusterList &matchedClusters, FragmentOverlapResult &fragmentOverlapResult) const
523 {
524  float chi2Sum(0.f);
525  unsigned int nMatchedSamplingPoints(0);
526 
527  CaloHitVector sortedMatchedHits(matchedHits.begin(), matchedHits.end());
528  std::sort(sortedMatchedHits.begin(), sortedMatchedHits.end(), LArClusterHelper::SortHitsByPosition);
529 
530  for (const CartesianVector &projectedPosition : projectedPositions)
531  {
532  float closestDistanceSquared(std::numeric_limits<float>::max());
533 
534  for (const CaloHit *const pCaloHit : matchedHits)
535  {
536  const float distanceSquared((pCaloHit->GetPositionVector() - projectedPosition).GetMagnitudeSquared());
537 
538  if (distanceSquared < closestDistanceSquared)
539  closestDistanceSquared = distanceSquared;
540  }
541 
542  if (closestDistanceSquared < m_maxPointDisplacementSquared)
543  {
544  ++nMatchedSamplingPoints;
545  chi2Sum += closestDistanceSquared;
546  }
547  }
548 
549  fragmentOverlapResult = FragmentOverlapResult(nMatchedSamplingPoints, projectedPositions.size(), chi2Sum, matchedHits, matchedClusters);
550 }
551 
552 //------------------------------------------------------------------------------------------------------------------------------------------
553 
554 bool ThreeViewTrackFragmentsAlgorithm::CheckMatchedClusters(const CartesianPointVector &projectedPositions, const ClusterList &matchedClusters) const
555 {
556  if (projectedPositions.empty() || matchedClusters.empty())
557  return false;
558 
559  // Calculate X and Z span of projected positions
560  float minXproj(+std::numeric_limits<float>::max());
561  float maxXproj(-std::numeric_limits<float>::max());
562  float minZproj(+std::numeric_limits<float>::max());
563  float maxZproj(-std::numeric_limits<float>::max());
564 
565  for (const CartesianVector &projectedPosition : projectedPositions)
566  {
567  minXproj = std::min(minXproj, projectedPosition.GetX());
568  maxXproj = std::max(maxXproj, projectedPosition.GetX());
569  minZproj = std::min(minZproj, projectedPosition.GetZ());
570  maxZproj = std::max(maxZproj, projectedPosition.GetZ());
571  }
572 
573  const float dXproj(maxXproj - minXproj);
574  const float dZproj(maxZproj - minZproj);
575  const float projectedLengthSquared(dXproj * dXproj + dZproj * dZproj);
576 
577  // Calculate X and Z span of matched clusters
578  float minXcluster(+std::numeric_limits<float>::max());
579  float maxXcluster(-std::numeric_limits<float>::max());
580  float minZcluster(+std::numeric_limits<float>::max());
581  float maxZcluster(-std::numeric_limits<float>::max());
582 
583  for (const Cluster *const pCluster : matchedClusters)
584  {
585  CartesianVector minPosition(0.f, 0.f, 0.f);
586  CartesianVector maxPosition(0.f, 0.f, 0.f);
587 
588  LArClusterHelper::GetClusterBoundingBox(pCluster, minPosition, maxPosition);
589 
590  minXcluster = std::min(minXcluster, minPosition.GetX());
591  maxXcluster = std::max(maxXcluster, maxPosition.GetX());
592  minZcluster = std::min(minZcluster, minPosition.GetZ());
593  maxZcluster = std::max(maxZcluster, maxPosition.GetZ());
594  }
595 
596  const float dXcluster(maxXcluster - minXcluster);
597  const float dZcluster(maxZcluster - minZcluster);
598  const float clusterLengthSquared(dXcluster * dXcluster + dZcluster * dZcluster);
599 
600  // Require that the span of the matched clusters is no larger than twice the span of the projected positions
601  if (clusterLengthSquared > 4.f * projectedLengthSquared)
602  return false;
603 
604  return true;
605 }
606 
607 //------------------------------------------------------------------------------------------------------------------------------------------
608 
610 {
611  // ATTN This method is currently mirrored in ClearTrackFragments tool
613  return false;
614 
615  if (overlapResult.GetFragmentCaloHitList().size() < m_minMatchedHits)
616  return false;
617 
618  return true;
619 }
620 
621 //------------------------------------------------------------------------------------------------------------------------------------------
622 
624 {
625  unsigned int repeatCounter(0);
626 
627  for (TensorToolVector::const_iterator iter = m_algorithmToolVector.begin(), iterEnd = m_algorithmToolVector.end(); iter != iterEnd;)
628  {
629  if ((*iter)->Run(this, this->GetMatchingControl().GetOverlapTensor()))
630  {
631  iter = m_algorithmToolVector.begin();
632 
633  if (++repeatCounter > m_nMaxTensorToolRepeats)
634  break;
635  }
636  else
637  {
638  ++iter;
639  }
640  }
641 }
642 
643 //------------------------------------------------------------------------------------------------------------------------------------------
644 
645 StatusCode ThreeViewTrackFragmentsAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
646 {
647  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ProcessAlgorithm(*this, xmlHandle, "ClusterRebuilding", m_reclusteringAlgorithmName));
648 
649  AlgorithmToolVector algorithmToolVector;
650  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ProcessAlgorithmToolList(*this, xmlHandle, "TrackTools", algorithmToolVector));
651 
652  for (AlgorithmToolVector::const_iterator iter = algorithmToolVector.begin(), iterEnd = algorithmToolVector.end(); iter != iterEnd; ++iter)
653  {
654  FragmentTensorTool *const pFragmentTensorTool(dynamic_cast<FragmentTensorTool *>(*iter));
655 
656  if (!pFragmentTensorTool)
657  return STATUS_CODE_INVALID_PARAMETER;
658 
659  m_algorithmToolVector.push_back(pFragmentTensorTool);
660  }
661 
662  PANDORA_RETURN_RESULT_IF_AND_IF(
663  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "NMaxTensorToolRepeats", m_nMaxTensorToolRepeats));
664 
665  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinXOverlap", m_minXOverlap));
666 
667  PANDORA_RETURN_RESULT_IF_AND_IF(
668  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinXOverlapFraction", m_minXOverlapFraction));
669 
670  float maxPointDisplacement = std::sqrt(m_maxPointDisplacementSquared);
671  PANDORA_RETURN_RESULT_IF_AND_IF(
672  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxPointDisplacement", maxPointDisplacement));
673  m_maxPointDisplacementSquared = maxPointDisplacement * maxPointDisplacement;
674 
675  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
676  XmlHelper::ReadValue(xmlHandle, "MinMatchedSamplingPointFraction", m_minMatchedSamplingPointFraction));
677 
678  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinMatchedHits", m_minMatchedHits));
679 
680  return BaseAlgorithm::ReadSettings(xmlHandle);
681 }
682 
683 } // namespace lar_content
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
pandora::ClusterList m_clusterListW
The selected modified cluster list W.
pandora::ClusterList m_clusterListU
The selected modified cluster list U.
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.
FragmentOverlapResult class.
void AddToSlidingFitCache(const pandora::Cluster *const pCluster)
Add a new sliding fit result, for the specified cluster, to the algorithm cache.
void RebuildClusters(const pandora::ClusterList &rebuildList, pandora::ClusterList &newClusters) const
Rebuild clusters after fragmentation.
void GetFragmentOverlapResult(const pandora::CartesianPointVector &projectedPositions, const pandora::CaloHitList &matchedHits, const pandora::ClusterList &matchedClusters, FragmentOverlapResult &fragmentOverlapResult) const
Get the populated fragment overlap result.
static void MergeTwoPositions3D(const pandora::Pandora &pandora, const pandora::HitType view1, const pandora::HitType view2, const pandora::CartesianVector &position1, const pandora::CartesianVector &position2, pandora::CartesianVector &position3D, float &chiSquared)
Merge 2D positions from two views to give unified 3D position.
pandora::ClusterList m_clusterListV
The selected modified cluster list V.
enum cvn::HType HitType
void PerformMainLoop()
Main loop over cluster combinations in order to populate the overlap container. Responsible for calli...
bool IsInitialized() const
Whether the track overlap result has been initialized.
std::string string
Definition: nybbler.cc:12
pandora::StatusCode GetMatchedClusters(const pandora::CaloHitList &matchedHits, const HitToClusterMap &hitToClusterMap, pandora::ClusterList &matchedClusters, const pandora::Cluster *&pBestMatchedCluster) const
Get the list of the relevant clusters and the address of the single best matched cluster.
const TwoDSlidingFitResult & GetCachedSlidingFitResult(const pandora::Cluster *const pCluster) const
Get a sliding fit result from the algorithm cache.
pandora::StatusCode GetProjectedPositions(const TwoDSlidingFitResult &fitResult1, const TwoDSlidingFitResult &fitResult2, pandora::CartesianPointVector &projectedPositions) const
Get the list of projected positions, in the third view, corresponding to a pair of sliding fit result...
std::unordered_map< const pandora::Cluster *, unsigned int > ClusterToMatchedHitsMap
static pandora::CartesianVector ProjectPosition(const pandora::Pandora &pandora, const pandora::CartesianVector &position3D, const pandora::HitType view)
Project 3D position into a given 2D view.
std::set< const gar::rec::Track * > TrackList
Definition: TrackCreator.h:14
unsigned int m_minMatchedHits
minimum number of matched calo hits
intermediate_table::const_iterator const_iterator
void ReplaceOverlapResult(const pandora::Cluster *const pClusterU, const pandora::Cluster *const pClusterV, const pandora::Cluster *const pClusterW, const OverlapResult &overlapResult)
SetReplace an existing overlap result.
static pandora::HitType GetClusterHitType(const pandora::Cluster *const pCluster)
Get the hit type associated with a two dimensional cluster.
static float GetWireZPitch(const pandora::Pandora &pandora, const float maxWirePitchDiscrepancy=0.01)
Return the wire pitch.
void ExamineOverlapContainer()
Examine contents of overlap container, collect together best-matching 2D particles and modify cluster...
Header file for the geometry helper class.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
void UpdateForNewCluster(const pandora::Cluster *const pNewCluster)
Update to reflect addition of a new cluster to the problem space.
static void GetClusterBoundingBox(const pandora::Cluster *const pCluster, pandora::CartesianVector &minimumCoordinate, pandora::CartesianVector &maximumCoordinate)
Get minimum and maximum X, Y and Z positions of the calo hits in a cluster.
const pandora::CaloHitList & GetFragmentCaloHitList() const
Get the list of fragment-associated hits.
void SetOverlapResult(const pandora::Cluster *const pClusterU, const pandora::Cluster *const pClusterV, const pandora::Cluster *const pClusterW, const OverlapResult &overlapResult)
Set overlap result.
bool CheckOverlapResult(const FragmentOverlapResult &overlapResult) const
Whether the matched clusters and hits pass the algorithm quality cuts.
static bool SortHitsByPosition(const pandora::CaloHit *const pLhs, const pandora::CaloHit *const pRhs)
Sort calo hits by their position (use Z, followed by X, followed by Y)
Header file for the cluster helper class.
std::string m_reclusteringAlgorithmName
Name of daughter algorithm to use for cluster re-building.
float GetMatchedFraction() const
Get the fraction of sampling points resulting in a match.
const pandora::ClusterList & GetInputClusterList(const pandora::HitType hitType) const
static float MergeTwoPositions(const pandora::Pandora &pandora, const pandora::HitType view1, const pandora::HitType view2, const float position1, const float position2)
Merge two views (U,V) to give a third view (Z).
const FitSegment & GetFitSegment(const float rL) const
Get fit segment for a given longitudinal coordinate.
pandora::CartesianVector GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.
static int max(int a, int b)
Header file for the three view fragments algorithm base class.
void CalculateOverlapResult(const pandora::Cluster *const pClusterU, const pandora::Cluster *const pClusterV, const pandora::Cluster *const pClusterW)
Calculate cluster overlap result and store in container.
bool CheckMatchedClusters(const pandora::CartesianPointVector &projectedPositions, const pandora::ClusterList &matchedClusters) const
Whether the matched clusters are consistent with the projected positions.
const pandora::ClusterList & GetSelectedClusterList(const pandora::HitType hitType) const
const pandora::Cluster * GetCluster() const
Get the address of the cluster, if originally provided.
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
const OverlapResult & GetOverlapResult(const pandora::Cluster *const pClusterU, const pandora::Cluster *const pClusterV, const pandora::Cluster *const pClusterW) const
Get the overlap result for a specified trio of clusters.
pandora::StatusCode GetGlobalFitProjection(const pandora::CartesianVector &inputPosition, pandora::CartesianVector &projectedPosition) const
Get projected position on global fit for a given position vector.
TensorToolVector m_algorithmToolVector
The algorithm tool list.
pandora::StatusCode GetTransverseProjection(const float x, const FitSegment &fitSegment, pandora::CartesianVector &position) const
Get projected position for a given input x coordinate and fit segment.
list x
Definition: train.py:276
std::vector< art::Ptr< recob::Cluster > > ClusterVector
void GetLocalPosition(const pandora::CartesianVector &position, float &rL, float &rT) const
Get local sliding fit coordinates for a given global position.
std::unordered_map< const pandora::CaloHit *, const pandora::Cluster * > HitToClusterMap
pandora::CartesianVector GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
unsigned int m_nMaxTensorToolRepeats
The maximum number of repeat loops over tensor tools.
float m_minXOverlapFraction
requirement on minimum X overlap fraction for associated clusters
float m_maxPointDisplacementSquared
maximum allowed distance (squared) between projected points and associated hits
float m_minXOverlap
requirement on minimum X overlap for associated clusters
float m_minMatchedSamplingPointFraction
minimum fraction of matched sampling points
pandora::StatusCode GetMatchedHits(const pandora::ClusterList &inputClusterList, const pandora::CartesianPointVector &projectedPositions, HitToClusterMap &hitToClusterMap, pandora::CaloHitList &matchedCaloHits) const
Get the list of hits associated with the projected positions and a useful hit to cluster map...