SlidingConePfoMopUpAlgorithm.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArThreeDReco/LArPfoMopUp/SlidingConePfoMopUpAlgorithm.cc
3  *
4  * @brief Implementation of the sliding cone pfo mop up algorithm class.
5  *
6  * $Log: $
7  */
8 
9 #include "Pandora/AlgorithmHeaders.h"
10 
15 
17 
19 
20 using namespace pandora;
21 
22 namespace lar_content
23 {
24 
25 SlidingConePfoMopUpAlgorithm::SlidingConePfoMopUpAlgorithm() :
26  m_useVertex(true),
27  m_maxIterations(1000),
28  m_maxHitsToConsider3DTrack(100),
29  m_minHitsToConsider3DShower(20),
30  m_halfWindowLayers(20),
31  m_nConeFitLayers(20),
32  m_nConeFits(5),
33  m_coneLengthMultiplier(7.f),
34  m_maxConeLength(126.f),
35  m_coneTanHalfAngle1(0.5f),
36  m_coneBoundedFraction1(0.5f),
37  m_coneTanHalfAngle2(0.75f),
38  m_coneBoundedFraction2(0.75f),
39  m_minVertexLongitudinalDistance(-2.5f),
40  m_maxVertexTransverseDistance(3.5f)
41 {
42 }
43 
44 //------------------------------------------------------------------------------------------------------------------------------------------
45 
47 {
48  const Vertex *pVertex(nullptr);
49  this->GetInteractionVertex(pVertex);
50 
51  if (m_useVertex && !pVertex)
52  {
53  if (PandoraContentApi::GetSettings(*this)->ShouldDisplayAlgorithmInfo())
54  std::cout << "SlidingConePfoMopUpAlgorithm - interaction vertex not available for use." << std::endl;
55  return STATUS_CODE_SUCCESS;
56  }
57 
58  unsigned int nIterations(0);
59 
60  while (nIterations++ < m_maxIterations)
61  {
62  ClusterVector clusters3D;
63  ClusterToPfoMap clusterToPfoMap;
64  this->GetThreeDClusters(clusters3D, clusterToPfoMap);
65 
66  ClusterMergeMap clusterMergeMap;
67  this->GetClusterMergeMap(pVertex, clusters3D, clusterToPfoMap, clusterMergeMap);
68 
69  if (!this->MakePfoMerges(clusterToPfoMap, clusterMergeMap))
70  break;
71  }
72 
73  return STATUS_CODE_SUCCESS;
74 }
75 
76 //------------------------------------------------------------------------------------------------------------------------------------------
77 
79 {
80  if (!m_useVertex)
81  return;
82 
83  const VertexList *pVertexList = nullptr;
84  PANDORA_THROW_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_INITIALIZED, !=, PandoraContentApi::GetCurrentList(*this, pVertexList));
85 
86  pVertex =
87  ((pVertexList && (pVertexList->size() == 1) && (VERTEX_3D == (*(pVertexList->begin()))->GetVertexType())) ? *(pVertexList->begin()) : nullptr);
88 }
89 
90 //------------------------------------------------------------------------------------------------------------------------------------------
91 
93 {
94  for (const std::string &pfoListName : m_inputPfoListNames)
95  {
96  const PfoList *pPfoList(nullptr);
97 
98  if (STATUS_CODE_SUCCESS != PandoraContentApi::GetList(*this, pfoListName, pPfoList))
99  continue;
100 
101  for (const Pfo *const pPfo : *pPfoList)
102  {
103  ClusterList pfoClusters3D;
104  LArPfoHelper::GetThreeDClusterList(pPfo, pfoClusters3D);
105 
106  for (const Cluster *const pCluster3D : pfoClusters3D)
107  {
108  if (LArPfoHelper::IsTrack(pPfo) && (pCluster3D->GetNCaloHits() > m_maxHitsToConsider3DTrack))
109  continue;
110 
111  if (!clusterToPfoMap.insert(ClusterToPfoMap::value_type(pCluster3D, pPfo)).second)
112  throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
113 
114  clusters3D.push_back(pCluster3D);
115  }
116  }
117  }
118 
119  std::sort(clusters3D.begin(), clusters3D.end(), LArClusterHelper::SortByNHits);
120 }
121 
122 //------------------------------------------------------------------------------------------------------------------------------------------
123 
124 void SlidingConePfoMopUpAlgorithm::GetClusterMergeMap(const Vertex *const pVertex, const ClusterVector &clusters3D,
125  const ClusterToPfoMap &clusterToPfoMap, ClusterMergeMap &clusterMergeMap) const
126 {
127  VertexAssociationMap vertexAssociationMap;
128  const float layerPitch(LArGeometryHelper::GetWireZPitch(this->GetPandora()));
129 
130  for (const Cluster *const pShowerCluster : clusters3D)
131  {
132  if ((pShowerCluster->GetNCaloHits() < m_minHitsToConsider3DShower) || !LArPfoHelper::IsShower(clusterToPfoMap.at(pShowerCluster)))
133  continue;
134 
135  float coneLength(0.f);
136  SimpleConeList simpleConeList;
137  bool isShowerVertexAssociated(false);
138 
139  try
140  {
141  const ThreeDSlidingConeFitResult slidingConeFitResult3D(pShowerCluster, m_halfWindowLayers, layerPitch);
142 
143  const CartesianVector &minLayerPosition(slidingConeFitResult3D.GetSlidingFitResult().GetGlobalMinLayerPosition());
144  const CartesianVector &maxLayerPosition(slidingConeFitResult3D.GetSlidingFitResult().GetGlobalMaxLayerPosition());
145  coneLength = std::min(m_coneLengthMultiplier * (maxLayerPosition - minLayerPosition).GetMagnitude(), m_maxConeLength);
146 
147  const float vertexToMinLayer(!pVertex ? 0.f : (pVertex->GetPosition() - minLayerPosition).GetMagnitude());
148  const float vertexToMaxLayer(!pVertex ? 0.f : (pVertex->GetPosition() - maxLayerPosition).GetMagnitude());
149  const ConeSelection coneSelection(
150  !pVertex ? CONE_BOTH_DIRECTIONS : (vertexToMaxLayer > vertexToMinLayer) ? CONE_FORWARD_ONLY : CONE_BACKWARD_ONLY);
151 
152  slidingConeFitResult3D.GetSimpleConeList(m_nConeFitLayers, m_nConeFits, coneSelection, simpleConeList);
153  isShowerVertexAssociated =
154  this->IsVertexAssociated(pShowerCluster, pVertex, vertexAssociationMap, &(slidingConeFitResult3D.GetSlidingFitResult()));
155  }
156  catch (const StatusCodeException &)
157  {
158  continue;
159  }
160 
161  for (const Cluster *const pNearbyCluster : clusters3D)
162  {
163  if (pNearbyCluster == pShowerCluster)
164  continue;
165 
166  ClusterMerge bestClusterMerge(nullptr, 0.f, 0.f);
167 
168  for (const SimpleCone &simpleCone : simpleConeList)
169  {
170  const float boundedFraction1(simpleCone.GetBoundedHitFraction(pNearbyCluster, coneLength, m_coneTanHalfAngle1));
171  const float boundedFraction2(simpleCone.GetBoundedHitFraction(pNearbyCluster, coneLength, m_coneTanHalfAngle2));
172  const ClusterMerge clusterMerge(pShowerCluster, boundedFraction1, boundedFraction2);
173 
174  if (clusterMerge < bestClusterMerge)
175  bestClusterMerge = clusterMerge;
176  }
177 
178  if (isShowerVertexAssociated && this->IsVertexAssociated(pNearbyCluster, pVertex, vertexAssociationMap))
179  continue;
180 
181  if (bestClusterMerge.GetParentCluster() && (bestClusterMerge.GetBoundedFraction1() > m_coneBoundedFraction1) &&
182  (bestClusterMerge.GetBoundedFraction2() > m_coneBoundedFraction2))
183  clusterMergeMap[pNearbyCluster].push_back(bestClusterMerge);
184  }
185  }
186 
187  for (ClusterMergeMap::value_type &mapEntry : clusterMergeMap)
188  std::sort(mapEntry.second.begin(), mapEntry.second.end());
189 }
190 
191 //------------------------------------------------------------------------------------------------------------------------------------------
192 
193 bool SlidingConePfoMopUpAlgorithm::IsVertexAssociated(const Cluster *const pCluster, const Vertex *const pVertex,
194  VertexAssociationMap &vertexAssociationMap, const ThreeDSlidingFitResult *const pSlidingFitResult) const
195 {
196  if (!pVertex)
197  return false;
198 
199  VertexAssociationMap::const_iterator iter = vertexAssociationMap.find(pCluster);
200 
201  if (vertexAssociationMap.end() != iter)
202  return iter->second;
203 
204  const bool isVertexAssociated(this->IsVertexAssociated(pCluster, pVertex->GetPosition(), pSlidingFitResult));
205  (void)vertexAssociationMap.insert(VertexAssociationMap::value_type(pCluster, isVertexAssociated));
206 
207  return isVertexAssociated;
208 }
209 
210 //------------------------------------------------------------------------------------------------------------------------------------------
211 
213  const Cluster *const pCluster, const CartesianVector &vertexPosition, const ThreeDSlidingFitResult *const pSlidingFitResult) const
214 {
215  try
216  {
217  const float layerPitch(LArGeometryHelper::GetWireZPitch(this->GetPandora()));
218  const LArPointingCluster pointingCluster(
219  pSlidingFitResult ? LArPointingCluster(*pSlidingFitResult) : LArPointingCluster(pCluster, m_halfWindowLayers, layerPitch));
220 
221  const bool useInner((pointingCluster.GetInnerVertex().GetPosition() - vertexPosition).GetMagnitudeSquared() <
222  (pointingCluster.GetOuterVertex().GetPosition() - vertexPosition).GetMagnitudeSquared());
223 
224  const LArPointingCluster::Vertex &daughterVertex(useInner ? pointingCluster.GetInnerVertex() : pointingCluster.GetOuterVertex());
226  }
227  catch (const StatusCodeException &)
228  {
229  }
230 
231  return false;
232 }
233 
234 //------------------------------------------------------------------------------------------------------------------------------------------
235 
236 bool SlidingConePfoMopUpAlgorithm::MakePfoMerges(const ClusterToPfoMap &clusterToPfoMap, const ClusterMergeMap &clusterMergeMap) const
237 {
238  ClusterVector daughterClusters;
239  for (const ClusterMergeMap::value_type &mapEntry : clusterMergeMap)
240  daughterClusters.push_back(mapEntry.first);
241  std::sort(daughterClusters.begin(), daughterClusters.end(), LArClusterHelper::SortByNHits);
242 
243  bool pfosMerged(false);
244  ClusterReplacementMap clusterReplacementMap;
245 
246  for (ClusterVector::const_reverse_iterator rIter = daughterClusters.rbegin(), rIterEnd = daughterClusters.rend(); rIter != rIterEnd; ++rIter)
247  {
248  const Cluster *const pDaughterCluster(*rIter);
249 
250  if (clusterReplacementMap.count(pDaughterCluster))
251  throw StatusCodeException(STATUS_CODE_FAILURE);
252 
253  const Cluster *pParentCluster(clusterMergeMap.at(pDaughterCluster).at(0).GetParentCluster());
254 
255  if (clusterReplacementMap.count(pParentCluster))
256  pParentCluster = clusterReplacementMap.at(pParentCluster);
257 
258  // ATTN Sign there was a reciprocal relationship in the cluster merge map (already actioned once)
259  if (pDaughterCluster == pParentCluster)
260  continue;
261 
262  // Key book-keeping on clusters and use cluster->pfo lookup
263  const Pfo *const pDaughterPfo(clusterToPfoMap.at(pDaughterCluster));
264  const Pfo *const pParentPfo(clusterToPfoMap.at(pParentCluster));
265  this->MergeAndDeletePfos(pParentPfo, pDaughterPfo);
266  pfosMerged = true;
267 
268  // Simple/placeholder book-keeping for reciprocal relationships and progressive merges
269  clusterReplacementMap[pDaughterCluster] = pParentCluster;
270 
271  for (ClusterReplacementMap::value_type &mapEntry : clusterReplacementMap)
272  {
273  if (pDaughterCluster == mapEntry.second)
274  mapEntry.second = pParentCluster;
275  }
276  }
277 
278  return pfosMerged;
279 }
280 
281 //------------------------------------------------------------------------------------------------------------------------------------------
282 //------------------------------------------------------------------------------------------------------------------------------------------
283 
285 {
286  if (!this->GetParentCluster() && !rhs.GetParentCluster())
287  return false;
288 
289  if (this->GetParentCluster() && !rhs.GetParentCluster())
290  return true;
291 
292  if (!this->GetParentCluster() && rhs.GetParentCluster())
293  return false;
294 
295  if (std::fabs(this->GetBoundedFraction1() - rhs.GetBoundedFraction1()) > std::numeric_limits<float>::epsilon())
296  return (this->GetBoundedFraction1() > rhs.GetBoundedFraction1());
297 
298  if (std::fabs(this->GetBoundedFraction2() - rhs.GetBoundedFraction2()) > std::numeric_limits<float>::epsilon())
299  return (this->GetBoundedFraction2() > rhs.GetBoundedFraction2());
300 
302 }
303 
304 //------------------------------------------------------------------------------------------------------------------------------------------
305 //------------------------------------------------------------------------------------------------------------------------------------------
306 
307 StatusCode SlidingConePfoMopUpAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
308 {
309  PANDORA_RETURN_RESULT_IF_AND_IF(
310  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadVectorOfValues(xmlHandle, "InputPfoListNames", m_inputPfoListNames));
311 
312  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "UseVertex", m_useVertex));
313 
314  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxIterations", m_maxIterations));
315 
316  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
317  XmlHelper::ReadValue(xmlHandle, "MaxHitsToConsider3DTrack", m_maxHitsToConsider3DTrack));
318 
319  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
320  XmlHelper::ReadValue(xmlHandle, "MinHitsToConsider3DShower", m_minHitsToConsider3DShower));
321 
322  PANDORA_RETURN_RESULT_IF_AND_IF(
323  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SlidingFitHalfWindow", m_halfWindowLayers));
324 
325  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "NConeFitLayers", m_nConeFitLayers));
326 
327  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "NConeFits", m_nConeFits));
328 
329  PANDORA_RETURN_RESULT_IF_AND_IF(
330  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ConeLengthMultiplier", m_coneLengthMultiplier));
331 
332  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxConeLength", m_maxConeLength));
333 
334  PANDORA_RETURN_RESULT_IF_AND_IF(
335  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ConeTanHalfAngle1", m_coneTanHalfAngle1));
336 
337  PANDORA_RETURN_RESULT_IF_AND_IF(
338  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ConeBoundedFraction1", m_coneBoundedFraction1));
339 
340  PANDORA_RETURN_RESULT_IF_AND_IF(
341  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ConeTanHalfAngle2", m_coneTanHalfAngle2));
342 
343  PANDORA_RETURN_RESULT_IF_AND_IF(
344  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ConeBoundedFraction2", m_coneBoundedFraction2));
345 
346  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
347  XmlHelper::ReadValue(xmlHandle, "MinVertexLongitudinalDistance", m_minVertexLongitudinalDistance));
348 
349  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
350  XmlHelper::ReadValue(xmlHandle, "MaxVertexTransverseDistance", m_maxVertexTransverseDistance));
351 
353 
354  return PfoMopUpBaseAlgorithm::ReadSettings(xmlHandle);
355 }
356 
357 } // namespace lar_content
std::unordered_map< const pandora::Cluster *, const pandora::Cluster * > ClusterReplacementMap
float GetBoundedFraction2() const
Get the bounded fraction for algorithm-specified cone angle 2.
void GetClusterMergeMap(const pandora::Vertex *const pVertex, const pandora::ClusterVector &clusters3D, const ClusterToPfoMap &clusterToPfoMap, ClusterMergeMap &clusterMergeMap) const
Get the cluster merge map describing all potential 3d cluster merges.
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.
float m_coneBoundedFraction2
The minimum cluster bounded fraction for association 2.
void GetInteractionVertex(const pandora::Vertex *&pVertex) const
Get the neutrino interaction vertex if it is available and if the algorithm is configured to do so...
Header file for the pfo helper class.
float m_maxConeLength
The maximum allowed cone length to use when calculating bounded cluster fractions.
float m_maxVertexTransverseDistance
Vertex association check: max transverse distance cut.
std::unordered_map< const pandora::Cluster *, const pandora::ParticleFlowObject * > ClusterToPfoMap
std::string string
Definition: nybbler.cc:12
float m_coneLengthMultiplier
The cone length multiplier to use when calculating bounded cluster fractions.
std::vector< SimpleCone > SimpleConeList
pandora::StringVector m_daughterListNames
The list of potential daughter object list names.
intermediate_table::const_iterator const_iterator
bool MakePfoMerges(const ClusterToPfoMap &clusterToPfoMap, const ClusterMergeMap &clusterMergeMap) const
Make pfo merges based on the provided cluster merge map.
const ThreeDSlidingFitResult & GetSlidingFitResult() const
Get the sliding fit result for the full cluster.
LArPointingCluster class.
void GetThreeDClusters(pandora::ClusterVector &clusters3D, ClusterToPfoMap &clusterToPfoMap) const
Get all 3d clusters contained in the input pfo lists and a mapping from clusters to pfos...
static bool IsTrack(const pandora::ParticleFlowObject *const pPfo)
Return track flag based on Pfo Particle ID.
static float GetWireZPitch(const pandora::Pandora &pandora, const float maxWirePitchDiscrepancy=0.01)
Return the wire pitch.
bool m_useVertex
Whether to use the interaction vertex to select useful cone directions.
Header file for the lar three dimensional sliding cone fit result class.
float GetBoundedFraction1() const
Get the bounded fraction for algorithm-specified cone angle 1.
void GetSimpleConeList(const unsigned int nLayersForConeFit, const unsigned int nCones, const ConeSelection coneSelection, SimpleConeList &simpleConeList) const
Get the list of simple cones fitted to the three dimensional cluster.
bool IsVertexAssociated(const pandora::Cluster *const pCluster, const pandora::Vertex *const pVertex, VertexAssociationMap &vertexAssociationMap, const ThreeDSlidingFitResult *const pSlidingFitResult=nullptr) const
Whether a 3D cluster is nodally associated with a provided vertex.
Header file for the geometry helper class.
float m_minVertexLongitudinalDistance
Vertex association check: min longitudinal distance cut.
static bool IsShower(const pandora::ParticleFlowObject *const pPfo)
Return shower flag based on Pfo Particle ID.
unsigned int m_nConeFitLayers
The number of layers over which to sum fitted direction to obtain cone fit.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
Header file for the cluster helper class.
const Vertex & GetOuterVertex() const
Get the outer vertex.
unsigned int m_maxHitsToConsider3DTrack
The maximum number of hits in a 3d track cluster to warrant inclusion in algorithm.
const Vertex & GetInnerVertex() const
Get the inner vertex.
float m_coneTanHalfAngle1
The cone tan half angle to use when calculating bounded cluster fractions 1.
pandora::StringVector m_inputPfoListNames
The input pfo list names.
Header file for the sliding cone pfo mop up algorithm class.
static const pandora::Cluster * GetParentCluster(const pandora::ClusterList &clusterList, const pandora::HitType hitType)
Select the parent cluster (same hit type and most hits) using a provided cluster list and hit type...
static void GetThreeDClusterList(const pandora::ParticleFlowObject *const pPfo, pandora::ClusterList &clusterList)
Get the list of 3D clusters from an input pfo.
const pandora::CartesianVector & GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
float m_coneBoundedFraction1
The minimum cluster bounded fraction for association 1.
std::unordered_map< const pandora::Cluster *, bool > VertexAssociationMap
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
unsigned int m_maxIterations
The maximum allowed number of algorithm iterations.
virtual void MergeAndDeletePfos(const pandora::ParticleFlowObject *const pPfoToEnlarge, const pandora::ParticleFlowObject *const pPfoToDelete) const
Merge and delete a pair of pfos, with a specific set of conventions for cluster merging, vertex use, etc.
ConeSelection
ConeSelection enum.
unsigned int m_minHitsToConsider3DShower
The minimum number of hits in a 3d shower cluster to attempt cone fits.
boost::graph_traits< ModuleGraph >::vertex_descriptor Vertex
Definition: ModuleGraph.h:25
std::vector< art::Ptr< recob::Cluster > > ClusterVector
bool operator<(const ClusterMerge &rhs) const
operator <
unsigned int m_nConeFits
The number of cone fits to perform, spread roughly uniformly along the shower length.
std::unordered_map< const pandora::Cluster *, ClusterMergeList > ClusterMergeMap
const pandora::Cluster * GetParentCluster() const
Get the address of the candidate parent (shower) cluster.
std::list< Vertex > VertexList
Definition: DCEL.h:182
const pandora::CartesianVector & GetPosition() const
Get the vertex position.
QTextStream & endl(QTextStream &s)
static bool IsNode(const pandora::CartesianVector &parentVertex, const LArPointingCluster::Vertex &daughterVertex, const float minLongitudinalDistance, const float maxTransverseDistance)
Whether pointing vertex is adjacent to a given position.
float m_coneTanHalfAngle2
The cone tan half angle to use when calculating bounded cluster fractions 2.
unsigned int m_halfWindowLayers
The number of layers to use for half-window of sliding fit.
const pandora::CartesianVector & GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.