LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
ClearTrackFragmentsTool.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 
12 
14 
15 using namespace pandora;
16 
17 namespace lar_content
18 {
19 
20 ClearTrackFragmentsTool::ClearTrackFragmentsTool() :
21  m_minMatchedSamplingPointFraction(0.5f),
22  m_minMatchedHits(5)
23 {
24 }
25 
26 //------------------------------------------------------------------------------------------------------------------------------------------
27 
29 {
30  if (PandoraContentApi::GetSettings(*pAlgorithm)->ShouldDisplayAlgorithmInfo())
31  std::cout << "----> Running Algorithm Tool: " << this->GetInstanceName() << ", " << this->GetType() << std::endl;
32 
33  return this->FindTrackFragments(pAlgorithm, overlapTensor);
34 }
35 
36 //------------------------------------------------------------------------------------------------------------------------------------------
37 
39 {
40  ClusterVector sortedKeyClusters;
41  overlapTensor.GetSortedKeyClusters(sortedKeyClusters);
42 
43  for (const Cluster *const pKeyCluster : sortedKeyClusters)
44  {
45  if (!pKeyCluster->IsAvailable())
46  continue;
47 
48  TensorType::ElementList elementList;
49  if (!this->GetAndCheckElementList(overlapTensor, pKeyCluster, elementList))
50  continue;
51 
52  IteratorList iteratorList;
53  this->SelectClearElements(elementList, iteratorList);
54 
55  if (iteratorList.empty())
56  return false;
57 
58  // ATTN Cache information as will later modify tensor during reclustering. Approach relies on updating tensor
59  // prior to reclustering operations and then exiting from tool (which will be scheduled to run again by algorithm)
60  TensorType::ElementList::const_iterator iter(iteratorList.front());
61 
62  const TensorType::OverlapResult overlapResult(iter->GetOverlapResult());
63  const HitType fragmentHitType(overlapResult.GetFragmentHitType());
64  const Cluster *pClusterU(iter->GetClusterU()), *pClusterV(iter->GetClusterV()), *pClusterW(iter->GetClusterW());
65 
66  if (!this->CheckOverlapResult(overlapResult))
67  continue;
68 
69  // ATTN No longer guaranteed safe to use iterator after processing tensor element, hence caching results above
70  const Cluster *pFragmentCluster(nullptr);
71  this->ProcessTensorElement(pAlgorithm, overlapTensor, overlapResult, pFragmentCluster);
72 
73  if (!pFragmentCluster)
74  throw StatusCodeException(STATUS_CODE_FAILURE);
75 
76  if (TPC_VIEW_U == fragmentHitType)
77  pClusterU = pFragmentCluster;
78  if (TPC_VIEW_V == fragmentHitType)
79  pClusterV = pFragmentCluster;
80  if (TPC_VIEW_W == fragmentHitType)
81  pClusterW = pFragmentCluster;
82 
83  if (!(pClusterU->IsAvailable() && pClusterV->IsAvailable() && pClusterW->IsAvailable()))
84  throw StatusCodeException(STATUS_CODE_FAILURE);
85 
86  // ATTN For safety, remove all clusters associated with this fragment particle from the tensor
87  ClusterList fragmentClusterList, affectedKeyClusters;
88  fragmentClusterList.push_back(pClusterU);
89  fragmentClusterList.push_back(pClusterV);
90  fragmentClusterList.push_back(pClusterW);
91  this->GetAffectedKeyClusters(overlapTensor, fragmentClusterList, affectedKeyClusters);
92 
93  for (const Cluster *const pCluster : affectedKeyClusters)
94  pAlgorithm->UpdateUponDeletion(pCluster);
95 
96  // Now make the particle
97  ProtoParticle protoParticle;
98  ProtoParticleVector protoParticleVector;
99  protoParticle.m_clusterList.push_back(pClusterU);
100  protoParticle.m_clusterList.push_back(pClusterV);
101  protoParticle.m_clusterList.push_back(pClusterW);
102  protoParticleVector.push_back(protoParticle);
103  return pAlgorithm->CreateThreeDParticles(protoParticleVector);
104  }
105 
106  return false;
107 }
108 
109 //------------------------------------------------------------------------------------------------------------------------------------------
110 
111 bool ClearTrackFragmentsTool::GetAndCheckElementList(const TensorType &overlapTensor, const Cluster *const pCluster, TensorType::ElementList &elementList) const
112 {
113  // Get list of connected elements from tensor
114  unsigned int nU(0), nV(0), nW(0);
115  overlapTensor.GetConnectedElements(pCluster, true, elementList, nU, nV, nW);
116 
117  // Only allow one fragment hit type
118  HitType fragmentHitType(HIT_CUSTOM);
119 
120  for (TensorType::ElementList::const_iterator eIter = elementList.begin(); eIter != elementList.end(); ++eIter)
121  {
122  const HitType thisHitType(eIter->GetOverlapResult().GetFragmentHitType());
123 
124  if (!((TPC_VIEW_U == thisHitType) || (TPC_VIEW_V == thisHitType) || (TPC_VIEW_W == thisHitType)))
125  throw StatusCodeException(STATUS_CODE_FAILURE);
126 
127  if (thisHitType != fragmentHitType && HIT_CUSTOM != fragmentHitType)
128  return false;
129 
130  fragmentHitType = thisHitType;
131  }
132 
133  return true;
134 }
135 
136 //------------------------------------------------------------------------------------------------------------------------------------------
137 
139 {
140  // ATTN This method is currently mirrored in ThreeViewTrackFragmentsAlgorithm algorithm
142  return false;
143 
144  if (overlapResult.GetFragmentCaloHitList().size() < m_minMatchedHits)
145  return false;
146 
147  return true;
148 }
149 
150 //------------------------------------------------------------------------------------------------------------------------------------------
151 
153 {
154  for (TensorType::ElementList::const_iterator eIter1 = elementList.begin(), eIterEnd1 = elementList.end(); eIter1 != eIterEnd1; ++eIter1)
155  {
156  const CaloHitList &fragmentHits1(eIter1->GetOverlapResult().GetFragmentCaloHitList());
157  const float nCaloHits1(static_cast<float>(
158  eIter1->GetClusterU()->GetNCaloHits() + eIter1->GetClusterV()->GetNCaloHits() + eIter1->GetClusterW()->GetNCaloHits()));
159 
160  bool isClearElement(true);
161 
162  for (TensorType::ElementList::const_iterator eIter2 = elementList.begin(), eIterEnd2 = elementList.end(); eIter2 != eIterEnd2; ++eIter2)
163  {
164  const CaloHitList &fragmentHits2(eIter2->GetOverlapResult().GetFragmentCaloHitList());
165  const float nCaloHits2(static_cast<float>(
166  eIter2->GetClusterU()->GetNCaloHits() + eIter2->GetClusterV()->GetNCaloHits() + eIter2->GetClusterW()->GetNCaloHits()));
167 
168  const bool commonClusterU(eIter1->GetClusterU() == eIter2->GetClusterU());
169  const bool commonClusterV(eIter1->GetClusterV() == eIter2->GetClusterV());
170  const bool commonClusterW(eIter1->GetClusterW() == eIter2->GetClusterW());
171 
172  if (commonClusterU && commonClusterV && commonClusterW)
173  continue;
174 
175  if (eIter1->GetOverlapResult().GetFragmentHitType() != eIter2->GetOverlapResult().GetFragmentHitType())
176  throw StatusCodeException(STATUS_CODE_FAILURE);
177 
178  bool isAmbiguousElement(commonClusterU || commonClusterV || commonClusterW);
179 
180  if (!isAmbiguousElement)
181  {
182  for (CaloHitList::const_iterator hIter2 = fragmentHits2.begin(), hIterEnd2 = fragmentHits2.end(); hIter2 != hIterEnd2; ++hIter2)
183  {
184  if (fragmentHits1.end() != std::find(fragmentHits1.begin(), fragmentHits1.end(), *hIter2))
185  {
186  isAmbiguousElement = true;
187  break;
188  }
189  }
190  }
191 
192  if (isAmbiguousElement && nCaloHits2 > 0.25f * nCaloHits1)
193  {
194  isClearElement = false;
195  break;
196  }
197  }
198 
199  if (isClearElement)
200  iteratorList.push_back(eIter1);
201  }
202 }
203 
204 //------------------------------------------------------------------------------------------------------------------------------------------
205 
207  const TensorType::OverlapResult &overlapResult, const Cluster *&pFragmentCluster) const
208 {
209  pFragmentCluster = nullptr;
210 
211  const HitType fragmentHitType(overlapResult.GetFragmentHitType());
212  const std::string &currentListName(pAlgorithm->GetClusterListName(fragmentHitType));
213 
214  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ReplaceCurrentList<Cluster>(*pAlgorithm, currentListName));
215 
216  ClusterList fragmentClusterList(overlapResult.GetFragmentClusterList());
217  fragmentClusterList.sort(LArClusterHelper::SortByNHits);
218  const CaloHitSet caloHitSet(overlapResult.GetFragmentCaloHitList().begin(), overlapResult.GetFragmentCaloHitList().end());
219 
220  // Remove any clusters to be modified (or affected by modifications) from tensor
221  ClusterList affectedKeyClusters;
222  this->GetAffectedKeyClusters(overlapTensor, fragmentClusterList, affectedKeyClusters);
223 
224  for (const Cluster *const pCluster : affectedKeyClusters)
225  pAlgorithm->UpdateUponDeletion(pCluster);
226 
227  for (const Cluster *const pCluster : fragmentClusterList)
228  pAlgorithm->UpdateUponDeletion(pCluster);
229 
230  ClusterList clustersToRebuild;
231  ClusterSet badClusters, deletedClusters;
232 
233  for (const Cluster *const pCluster : fragmentClusterList)
234  {
235  if (deletedClusters.count(pCluster))
236  continue;
237 
238  if (!pCluster->IsAvailable())
239  throw StatusCodeException(STATUS_CODE_FAILURE);
240 
241  CaloHitList clusterHitList;
242  pCluster->GetOrderedCaloHitList().FillCaloHitList(clusterHitList);
243 
244  CaloHitList daughterHits, separateHits;
245  for (const CaloHit *const pCaloHit : clusterHitList)
246  {
247  if (caloHitSet.count(pCaloHit))
248  {
249  daughterHits.push_back(pCaloHit);
250  }
251  else
252  {
253  separateHits.push_back(pCaloHit);
254  }
255  }
256 
257  if (daughterHits.empty())
258  throw StatusCodeException(STATUS_CODE_FAILURE);
259 
260  this->Recluster(pAlgorithm, pCluster, daughterHits, separateHits, deletedClusters, badClusters, pFragmentCluster);
261 
262  // ATTN Fragment cluster will be used to build particle, so it shouldn't ever be bad, or deleted
263  if (badClusters.count(pFragmentCluster) || deletedClusters.count(pFragmentCluster))
264  throw StatusCodeException(STATUS_CODE_FAILURE);
265 
266  // ATTN Keep track of clusters to be rebuilt; does not include those for which address has been deleted at any time.
267  // Note distinction between list of all deletions and the up-to-date list of bad clusters.
268  // Fragment cluster will be automatically added to the output particle and never rebuilt.
269  ClusterList::iterator rebuildIter(std::find(clustersToRebuild.begin(), clustersToRebuild.end(), pCluster));
270  if (deletedClusters.count(pCluster))
271  {
272  if (clustersToRebuild.end() != rebuildIter)
273  clustersToRebuild.erase(rebuildIter);
274  }
275  else if ((clustersToRebuild.end() == rebuildIter) && (pCluster != pFragmentCluster))
276  {
277  clustersToRebuild.push_back(pCluster);
278  }
279  }
280 
281  if (!pFragmentCluster)
282  throw StatusCodeException(STATUS_CODE_FAILURE);
283 
284  // Rebuild fragmented clusters into something better defined
285  ClusterList clustersToAddToTensor;
286  this->RebuildClusters(pAlgorithm, clustersToRebuild, clustersToAddToTensor);
287 
288  // ATTN Repopulate tensor according to modifications performed above
289  ClusterList newKeyClusters;
290  pAlgorithm->SelectInputClusters(&clustersToAddToTensor, newKeyClusters);
291 
292  for (const Cluster *const pCluster : newKeyClusters)
293  pAlgorithm->UpdateForNewCluster(pCluster);
294 
295  for (const Cluster *const pCluster : affectedKeyClusters)
296  pAlgorithm->UpdateForNewCluster(pCluster);
297 }
298 
299 //------------------------------------------------------------------------------------------------------------------------------------------
300 
301 void ClearTrackFragmentsTool::Recluster(ThreeViewTrackFragmentsAlgorithm *const pAlgorithm, const Cluster *const pCluster, const CaloHitList &daughterHits,
302  const CaloHitList &separateHits, ClusterSet &deletedClusters, ClusterSet &badClusters, const Cluster *&pFragmentCluster) const
303 {
304  if (separateHits.empty())
305  {
306  if (!pFragmentCluster)
307  {
308  pFragmentCluster = pCluster;
309  }
310  else
311  {
312  // ATTN Addresses can be re-used by new allocations, so can't just use list of all cluster deletions; keep track of "bad" clusters
313  if (!badClusters.insert(pCluster).second)
314  throw StatusCodeException(STATUS_CODE_FAILURE);
315 
316  (void)deletedClusters.insert(pCluster);
317  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::MergeAndDeleteClusters(*pAlgorithm, pFragmentCluster, pCluster));
318  }
319  }
320  else
321  {
322  for (const CaloHit *const pCaloHit : daughterHits)
323  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::RemoveFromCluster(*pAlgorithm, pCluster, pCaloHit));
324 
325  if (!pFragmentCluster)
326  {
327  const ClusterList *pTemporaryList(nullptr);
328  std::string temporaryListName, currentListName;
329  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentListName<Cluster>(*pAlgorithm, currentListName));
330  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=,
331  PandoraContentApi::CreateTemporaryListAndSetCurrent<ClusterList>(*pAlgorithm, pTemporaryList, temporaryListName));
332 
333  PandoraContentApi::Cluster::Parameters hitParameters;
334  hitParameters.m_caloHitList = daughterHits;
335  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::Cluster::Create(*pAlgorithm, hitParameters, pFragmentCluster));
336  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::SaveList<Cluster>(*pAlgorithm, temporaryListName, currentListName));
337  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ReplaceCurrentList<Cluster>(*pAlgorithm, currentListName));
338 
339  (void)badClusters.erase(pFragmentCluster);
340  }
341  else
342  {
343  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::AddToCluster(*pAlgorithm, pFragmentCluster, &daughterHits));
344  }
345  }
346 }
347 
348 //------------------------------------------------------------------------------------------------------------------------------------------
349 
351  ThreeViewTrackFragmentsAlgorithm *const pAlgorithm, const ClusterList &modifiedClusters, ClusterList &newClusters) const
352 {
353  ClusterList rebuildList;
354 
355  for (const Cluster *const pCluster : modifiedClusters)
356  {
357  if (pCluster->IsAvailable())
358  rebuildList.push_back(pCluster);
359  }
360 
361  if (!rebuildList.empty())
362  pAlgorithm->RebuildClusters(rebuildList, newClusters);
363 }
364 
365 //------------------------------------------------------------------------------------------------------------------------------------------
366 
368  const TensorType &overlapTensor, const ClusterList &clustersToRemoveFromTensor, ClusterList &affectedKeyClusters) const
369 {
370  for (TensorType::const_iterator tIterU = overlapTensor.begin(), tIterUEnd = overlapTensor.end(); tIterU != tIterUEnd; ++tIterU)
371  {
372  for (TensorType::OverlapMatrix::const_iterator tIterV = tIterU->second.begin(), tIterVEnd = tIterU->second.end(); tIterV != tIterVEnd; ++tIterV)
373  {
374  for (TensorType::OverlapList::const_iterator tIterW = tIterV->second.begin(), tIterWEnd = tIterV->second.end(); tIterW != tIterWEnd; ++tIterW)
375  {
376  const TensorType::OverlapResult &overlapResult(tIterW->second);
377  const HitType fragmentHitType(overlapResult.GetFragmentHitType());
378  const ClusterList &fragmentClusters(overlapResult.GetFragmentClusterList());
379 
380  for (ClusterList::const_iterator fIter = fragmentClusters.begin(), fIterEnd = fragmentClusters.end(); fIter != fIterEnd; ++fIter)
381  {
382  if (clustersToRemoveFromTensor.end() == std::find(clustersToRemoveFromTensor.begin(), clustersToRemoveFromTensor.end(), *fIter))
383  continue;
384 
385  if ((TPC_VIEW_U != fragmentHitType) &&
386  (affectedKeyClusters.end() == std::find(affectedKeyClusters.begin(), affectedKeyClusters.end(), tIterU->first)))
387  affectedKeyClusters.push_back(tIterU->first);
388 
389  if ((TPC_VIEW_V != fragmentHitType) &&
390  (affectedKeyClusters.end() == std::find(affectedKeyClusters.begin(), affectedKeyClusters.end(), tIterV->first)))
391  affectedKeyClusters.push_back(tIterV->first);
392 
393  if ((TPC_VIEW_W != fragmentHitType) &&
394  (affectedKeyClusters.end() == std::find(affectedKeyClusters.begin(), affectedKeyClusters.end(), tIterW->first)))
395  affectedKeyClusters.push_back(tIterW->first);
396 
397  break;
398  }
399  }
400  }
401  }
402 
403  affectedKeyClusters.sort(LArClusterHelper::SortByNHits);
404 }
405 
406 //------------------------------------------------------------------------------------------------------------------------------------------
407 
408 StatusCode ClearTrackFragmentsTool::ReadSettings(const TiXmlHandle xmlHandle)
409 {
410  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
411  XmlHelper::ReadValue(xmlHandle, "MinMatchedSamplingPointFraction", m_minMatchedSamplingPointFraction));
412 
413  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinMatchedHits", m_minMatchedHits));
414 
415  return STATUS_CODE_SUCCESS;
416 }
417 
418 } // namespace lar_content
intermediate_table::iterator iterator
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.
std::vector< ProtoParticle > ProtoParticleVector
FragmentOverlapResult class.
void RebuildClusters(const pandora::ClusterList &rebuildList, pandora::ClusterList &newClusters) const
Rebuild clusters after fragmentation.
unsigned int m_minMatchedHits
The minimum number of matched calo hits.
Header file for the clear track fragments tool class.
virtual void UpdateUponDeletion(const pandora::Cluster *const pDeletedCluster)
Update to reflect cluster deletion.
const_iterator begin() const
Returns an iterator referring to the first element in the overlap tensor.
void RebuildClusters(ThreeViewTrackFragmentsAlgorithm *const pAlgorithm, const pandora::ClusterList &modifiedClusters, pandora::ClusterList &newClusters) const
Rebuild clusters after fragmentation.
void GetConnectedElements(const pandora::Cluster *const pCluster, const bool ignoreUnavailable, ElementList &elementList) const
Get a list of elements connected to a specified cluster.
bool Run(ThreeViewTrackFragmentsAlgorithm *const pAlgorithm, TensorType &overlapTensor)
Run the algorithm tool.
intermediate_table::const_iterator const_iterator
float m_minMatchedSamplingPointFraction
The minimum fraction of matched sampling points.
const_iterator end() const
Returns an iterator referring to the past-the-end element in the overlap tensor.
TFile f
Definition: plotHisto.C:6
void Recluster(ThreeViewTrackFragmentsAlgorithm *const pAlgorithm, const pandora::Cluster *const pCluster, const pandora::CaloHitList &daughterHits, const pandora::CaloHitList &separateHits, pandora::ClusterSet &deletedClusters, pandora::ClusterSet &badClusters, const pandora::Cluster *&pFragmentCluster) const
Rearrange the hits in a cluster from the fragment list, using the Pandora fragmentation mechanism...
void UpdateForNewCluster(const pandora::Cluster *const pNewCluster)
Update to reflect addition of a new cluster to the problem space.
pandora::HitType GetFragmentHitType() const
Get the fragment hit type.
const pandora::CaloHitList & GetFragmentCaloHitList() const
Get the list of fragment-associated hits.
void ProcessTensorElement(ThreeViewTrackFragmentsAlgorithm *const pAlgorithm, const TensorType &overlapTensor, const TensorType::OverlapResult &overlapResult, const pandora::Cluster *&pFragmentCluster) const
Process a tensor element, reclustering the fragments as required.
bool FindTrackFragments(ThreeViewTrackFragmentsAlgorithm *const pAlgorithm, const TensorType &overlapTensor) const
Find suitable matching track fragments in the overlap tensor to use for 3D particle creation...
Header file for the cluster helper class.
std::vector< TensorType::ElementList::const_iterator > IteratorList
float GetMatchedFraction() const
Get the fraction of sampling points resulting in a match.
pandora::ClusterList m_clusterList
List of 2D clusters in a 3D proto particle.
void GetSortedKeyClusters(pandora::ClusterVector &sortedKeyClusters) const
Get a sorted vector of key clusters (U clusters with current implementation)
void SelectClearElements(const TensorType::ElementList &elementList, IteratorList &iteratorList) const
Select a list of clear track-like elements from a set of connected tensor elements.
bool GetAndCheckElementList(const TensorType &overlapTensor, const pandora::Cluster *const pCluster, TensorType::ElementList &elementList) const
Get the list of elements connected to a given cluster and check its suitability (no ambiguities) ...
void GetAffectedKeyClusters(const TensorType &overlapTensor, const pandora::ClusterList &clustersToRemoveFromTensor, pandora::ClusterList &affectedKeyClusters) const
Get a list of the tensor key clusters for which tensor elements have been impacted by fragmentation o...
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
HitType
Definition: HitType.h:12
bool CheckOverlapResult(const TensorType::OverlapResult &overlapResult) const
Check whether the overlap result passes matched sampling point and number of matched hit checks...
virtual void SelectInputClusters(const pandora::ClusterList *const pInputClusterList, pandora::ClusterList &selectedClusterList) const
Select a subset of input clusters for processing in this algorithm.
const std::string & GetClusterListName(const pandora::HitType hitType) const
Get the cluster list name corresponding to a specified hit type.
virtual bool CreateThreeDParticles(const ProtoParticleVector &protoParticleVector)
Create particles using findings from recent algorithm processing.
std::vector< art::Ptr< recob::Cluster > > ClusterVector
const pandora::ClusterList & GetFragmentClusterList() const
Get the list of fragment-associated clusters.