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