LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
CrossedTrackSplittingAlgorithm.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 
13 
15 
17 
18 using namespace pandora;
19 
20 namespace lar_content
21 {
22 
23 CrossedTrackSplittingAlgorithm::CrossedTrackSplittingAlgorithm() :
24  m_maxClusterSeparation(2.f),
25  m_maxClusterSeparationSquared(m_maxClusterSeparation * m_maxClusterSeparation),
26  m_minCosRelativeAngle(0.966f),
27  m_searchRegion1D(2.f)
28 {
29 }
30 
31 //------------------------------------------------------------------------------------------------------------------------------------------
32 
34 {
35  // ATTN Don't need to update nearby cluster map after cluster merges because algorithm does not revisit processed clusters
36  HitToClusterMap hitToClusterMap;
37  CaloHitList allCaloHits;
38 
39  for (const Cluster *const pCluster : clusterVector)
40  {
41  CaloHitList daughterHits;
42  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
43  allCaloHits.insert(allCaloHits.end(), daughterHits.begin(), daughterHits.end());
44 
45  for (const CaloHit *const pCaloHit : daughterHits)
46  (void) hitToClusterMap.insert(HitToClusterMap::value_type(pCaloHit, pCluster));
47  }
48 
49  HitKDTree2D kdTree;
50  HitKDNode2DList hitKDNode2DList;
51 
52  KDTreeBox hitsBoundingRegion2D(fill_and_bound_2d_kd_tree(allCaloHits, hitKDNode2DList));
53  kdTree.build(hitKDNode2DList, hitsBoundingRegion2D);
54 
55  for (const Cluster *const pCluster : clusterVector)
56  {
57  CaloHitList daughterHits;
58  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
59 
60  for (const CaloHit *const pCaloHit : daughterHits)
61  {
63 
64  HitKDNode2DList found;
65  kdTree.search(searchRegionHits, found);
66 
67  for (const auto &hit : found)
68  (void) m_nearbyClusters[pCluster].insert(hitToClusterMap.at(hit.data));
69  }
70  }
71 
72  return STATUS_CODE_SUCCESS;
73 }
74 
75 //------------------------------------------------------------------------------------------------------------------------------------------
76 
78 {
79  m_nearbyClusters.clear();
80 
81  return STATUS_CODE_SUCCESS;
82 }
83 
84 //------------------------------------------------------------------------------------------------------------------------------------------
85 
86 StatusCode CrossedTrackSplittingAlgorithm::FindBestSplitPosition(const TwoDSlidingFitResult &slidingFitResult1, const TwoDSlidingFitResult &slidingFitResult2,
87  CartesianVector &splitPosition, CartesianVector &firstDirection, CartesianVector &secondDirection) const
88 {
89  // Use cached results from kd-tree to avoid expensive calculations
90  if (!m_nearbyClusters.count(slidingFitResult1.GetCluster()) ||
91  !m_nearbyClusters.count(slidingFitResult2.GetCluster()) ||
92  !m_nearbyClusters.at(slidingFitResult1.GetCluster()).count(slidingFitResult2.GetCluster()) ||
93  !m_nearbyClusters.at(slidingFitResult2.GetCluster()).count(slidingFitResult1.GetCluster()))
94  {
95  return STATUS_CODE_NOT_FOUND;
96  }
97 
98  // Identify crossed-track topology and find candidate intersection positions
99  const CartesianVector& minPosition1(slidingFitResult1.GetGlobalMinLayerPosition());
100  const CartesianVector& maxPosition1(slidingFitResult1.GetGlobalMaxLayerPosition());
101 
102  const CartesianVector& minPosition2(slidingFitResult2.GetGlobalMinLayerPosition());
103  const CartesianVector& maxPosition2(slidingFitResult2.GetGlobalMaxLayerPosition());
104 
105  if (LArClusterHelper::GetClosestDistance(minPosition1, slidingFitResult2.GetCluster()) < 2.f * m_maxClusterSeparation ||
106  LArClusterHelper::GetClosestDistance(maxPosition1, slidingFitResult2.GetCluster()) < 2.f * m_maxClusterSeparation ||
107  LArClusterHelper::GetClosestDistance(minPosition2, slidingFitResult1.GetCluster()) < 2.f * m_maxClusterSeparation ||
108  LArClusterHelper::GetClosestDistance(maxPosition2, slidingFitResult1.GetCluster()) < 2.f * m_maxClusterSeparation)
109  {
110  return STATUS_CODE_NOT_FOUND;
111  }
112 
113  if (LArClusterHelper::GetClosestDistance(slidingFitResult1.GetCluster(), slidingFitResult2.GetCluster()) > m_maxClusterSeparation)
114  return STATUS_CODE_NOT_FOUND;
115 
116  CartesianPointVector candidateVector;
117  this->FindCandidateSplitPositions(slidingFitResult1.GetCluster(), slidingFitResult2.GetCluster(), candidateVector);
118 
119  if (candidateVector.empty())
120  return STATUS_CODE_NOT_FOUND;
121 
122 
123  // Loop over candidate positions and find best split position
124  bool foundSplit(false);
125  float closestSeparationSquared(std::numeric_limits<float>::max());
126 
127  const float halfWindowLength1(slidingFitResult1.GetLayerFitHalfWindowLength());
128  const float halfWindowLength2(slidingFitResult2.GetLayerFitHalfWindowLength());
129 
130  for (CartesianPointVector::const_iterator iter = candidateVector.begin(), iterEnd = candidateVector.end(); iter != iterEnd; ++iter)
131  {
132  const CartesianVector &candidatePosition(*iter);
133 
134  // Projections onto first cluster
135  float rL1(0.f), rT1(0.f);
136  CartesianVector R1(0.f, 0.f, 0.f);
137  CartesianVector F1(0.f, 0.f, 0.f);
138  CartesianVector B1(0.f, 0.f, 0.f);
139 
140  if (STATUS_CODE_SUCCESS != slidingFitResult1.GetGlobalFitProjection(candidatePosition, R1))
141  continue;
142 
143  slidingFitResult1.GetLocalPosition(R1, rL1, rT1);
144  if ((STATUS_CODE_SUCCESS != slidingFitResult1.GetGlobalFitPosition(rL1 + halfWindowLength1, F1)) ||
145  (STATUS_CODE_SUCCESS != slidingFitResult1.GetGlobalFitPosition(rL1 - halfWindowLength1, B1)))
146  {
147  continue;
148  }
149 
150  // Projections onto second cluster
151  float rL2(0.f), rT2(0.f);
152  CartesianVector R2(0.f, 0.f, 0.f);
153  CartesianVector F2(0.f, 0.f, 0.f);
154  CartesianVector B2(0.f, 0.f, 0.f);
155 
156  if (STATUS_CODE_SUCCESS != slidingFitResult2.GetGlobalFitProjection(candidatePosition, R2))
157  continue;
158 
159  slidingFitResult2.GetLocalPosition(R2, rL2, rT2);
160  if ((STATUS_CODE_SUCCESS != slidingFitResult2.GetGlobalFitPosition(rL2 + halfWindowLength2, F2)) ||
161  (STATUS_CODE_SUCCESS != slidingFitResult2.GetGlobalFitPosition(rL2 - halfWindowLength2, B2)))
162  {
163  continue;
164  }
165 
166  // Calculate average position
167  const CartesianVector C0((R1 + R2) * 0.5);
168 
169  // Calculate intersected position:
170  // ==============================
171  // First cluster gives set of points: B1->R1->F1
172  // Second cluster gives set of points: B2->R2->F2
173  //
174  // Try swapping B1 with B2 to see if this gives intersecting straight lines:
175  //
176  // F1 F2 a2 b1
177  // | | | |
178  // | | | |
179  // R1 R2 R1 R2
180  // | | | |
181  // | | | |
182  // B1 B2 a1 b2
183 
184  // First straight line is a1->R1->b1
185  // Second straight line is a2->R2->b2
186 
187  const CartesianVector a1(B1);
188  const CartesianVector a2(F1);
189 
190  for (unsigned int iForward = 0; iForward<2; ++iForward)
191  {
192  const CartesianVector b1((0 == iForward) ? F2 : B2);
193  const CartesianVector b2((0 == iForward) ? B2 : F2);
194 
195  const CartesianVector s1((b1 - R2).GetUnitVector());
196  const CartesianVector t1((R1 - a1).GetUnitVector());
197  const CartesianVector s2((b2 - R2).GetUnitVector());
198  const CartesianVector t2((R1 - a2).GetUnitVector());
199 
200  if (s1.GetDotProduct(t1) < std::max(m_minCosRelativeAngle,-s1.GetDotProduct(s2)) ||
201  s2.GetDotProduct(t2) < std::max(m_minCosRelativeAngle,-t1.GetDotProduct(t2)))
202  continue;
203 
204  const CartesianVector p1((b1 - a1).GetUnitVector());
205  const CartesianVector p2((b2 - a2).GetUnitVector());
206 
207  float mu1(0.f), mu2(0.f);
208  CartesianVector C1(0.f,0.f,0.f);
209 
210  try
211  {
212  LArPointingClusterHelper::GetIntersection(a1, p1, a2, p2, C1, mu1, mu2);
213  }
214  catch (const StatusCodeException &)
215  {
216  continue;
217  }
218 
219  if (mu1 < 0.f || mu2 < 0.f || mu1 > (b1 - a1).GetMagnitude() || mu2 > (b2 - a2).GetMagnitude())
220  continue;
221 
222  const float thisSeparationSquared((C0 - C1).GetMagnitudeSquared());
223 
224  if (thisSeparationSquared < closestSeparationSquared)
225  {
226  closestSeparationSquared = thisSeparationSquared;
227  splitPosition = (C0 + C1) * 0.5;
228  firstDirection = t2 * -1.f;
229  secondDirection = t1;
230  foundSplit = true;
231  }
232  }
233  }
234 
235  if (!foundSplit)
236  return STATUS_CODE_NOT_FOUND;
237 
238  return STATUS_CODE_SUCCESS;
239 }
240 
241 //------------------------------------------------------------------------------------------------------------------------------------------
242 
243 void CrossedTrackSplittingAlgorithm::FindCandidateSplitPositions(const Cluster *const pCluster1, const Cluster *const pCluster2,
244  CartesianPointVector &candidateVector) const
245 {
246  // ATTN The following is double-double counting
247  CaloHitList caloHitList1, caloHitList2;
248  pCluster1->GetOrderedCaloHitList().FillCaloHitList(caloHitList1);
249  pCluster2->GetOrderedCaloHitList().FillCaloHitList(caloHitList2);
250 
251  CaloHitVector caloHitVector1(caloHitList1.begin(), caloHitList1.end()), caloHitVector2(caloHitList2.begin(), caloHitList2.end());
252  std::sort(caloHitVector1.begin(), caloHitVector1.end(), LArClusterHelper::SortHitsByPosition);
253  std::sort(caloHitVector2.begin(), caloHitVector2.end(), LArClusterHelper::SortHitsByPosition);
254 
255  for (const CaloHit *const pCaloHit : caloHitVector1)
256  {
257  const CartesianVector position1(pCaloHit->GetPositionVector());
258  const CartesianVector position2(LArClusterHelper::GetClosestPosition(position1, pCluster2));
259 
260  if ((position1 - position2).GetMagnitudeSquared() < m_maxClusterSeparationSquared)
261  candidateVector.push_back((position1 + position2) * 0.5);
262  }
263 
264  for (const CaloHit *const pCaloHit : caloHitVector2)
265  {
266  const CartesianVector position2(pCaloHit->GetPositionVector());
267  const CartesianVector position1(LArClusterHelper::GetClosestPosition(position2, pCluster1));
268 
269  if ((position2 - position1).GetMagnitudeSquared() < m_maxClusterSeparationSquared)
270  candidateVector.push_back((position2 + position1) * 0.5);
271  }
272 }
273 
274 //------------------------------------------------------------------------------------------------------------------------------------------
275 
276 StatusCode CrossedTrackSplittingAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
277 {
278  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
279  "MaxClusterSeparation", m_maxClusterSeparation));
281 
282  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
283  "MinCosRelativeAngle", m_minCosRelativeAngle));
284 
285  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
286  "SearchRegion1D", m_searchRegion1D));
287 
289 }
290 
291 } // namespace lar_content
Header file for the kd tree linker algo template class.
pandora::StatusCode FindBestSplitPosition(const TwoDSlidingFitResult &slidingFit1, const TwoDSlidingFitResult &slidingFit2, pandora::CartesianVector &splitPosition, pandora::CartesianVector &direction1, pandora::CartesianVector &direction2) const
Find the best split position and direction for a pair of clusters.
TTree * t1
Definition: plottest35.C:26
static void GetIntersection(const LArPointingCluster::Vertex &firstVertex, const LArPointingCluster::Vertex &secondVertex, pandora::CartesianVector &intersectPosition, float &firstDisplacement, float &secondDisplacement)
Get intersection of two vertices.
Class that implements the KDTree partition of 2D space and a closest point search algorithm...
Box structure used to define 2D field. It&#39;s used in KDTree building step to divide the detector space...
Header file for the crossed track splitting algorithm class.
float GetLayerFitHalfWindowLength() const
Get the layer fit half window length.
float m_minCosRelativeAngle
maximum relative angle between tracks after un-crossing
float m_maxClusterSeparationSquared
maximum separation of two clusters (squared)
std::unordered_map< const pandora::CaloHit *, const pandora::Cluster * > HitToClusterMap
TFile f
Definition: plotHisto.C:6
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
#define a2
Int_t max
Definition: plot.C:27
void search(const KDTreeBoxT< DIM > &searchBox, std::vector< KDTreeNodeInfoT< DATA, DIM > > &resRecHitList)
Search in the KDTree for all points that would be contained in the given searchbox The founded points...
intermediate_table::const_iterator const_iterator
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.
pandora::StatusCode GetGlobalFitPosition(const float rL, pandora::CartesianVector &position) const
Get global fit position for a given longitudinal coordinate.
pandora::CartesianVector GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.
pandora::StatusCode TidyUpStep()
Tidy up any information cached in e.g. the preparation step.
void FindCandidateSplitPositions(const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2, pandora::CartesianPointVector &candidateVector) const
Find average positions of pairs of hits within a maximum separation.
Detector simulation of raw signals on wires.
TTree * t2
Definition: plottest35.C:36
std::vector< art::Ptr< recob::Cluster > > ClusterVector
const pandora::Cluster * GetCluster() const
Get the address of the cluster, if originally provided.
pandora::StatusCode PreparationStep(const pandora::ClusterVector &clusterVector)
Perform any preparatory actions, such as caching information for subsequent expensive calculations...
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
float m_maxClusterSeparation
maximum separation of two clusters
pandora::StatusCode GetGlobalFitProjection(const pandora::CartesianVector &inputPosition, pandora::CartesianVector &projectedPosition) const
Get projected position on global fit for a given position vector.
KDTreeBox fill_and_bound_2d_kd_tree(const MANAGED_CONTAINER< const T * > &points, std::vector< KDTreeNodeInfoT< const T *, 2 > > &nodes)
fill_and_bound_2d_kd_tree
ClusterToClustersMap m_nearbyClusters
The nearby clusters map.
static pandora::CartesianVector GetClosestPosition(const pandora::CartesianVector &position, const pandora::ClusterList &clusterList)
Get closest position in a list of clusters to a specified input position vector.
KDTreeBox build_2d_kd_search_region(const pandora::CaloHit *const point, const float x_span, const float z_span)
build_2d_kd_search_region
void GetLocalPosition(const pandora::CartesianVector &position, float &rL, float &rT) const
Get local sliding fit coordinates for a given global position.
float m_searchRegion1D
Search region, applied to each dimension, for look-up from kd-trees.
pandora::CartesianVector GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
void build(std::vector< KDTreeNodeInfoT< DATA, DIM > > &eltList, const KDTreeBoxT< DIM > &region)
Build the KD tree from the "eltList" in the space define by "region".
#define a1
static float GetClosestDistance(const pandora::ClusterList &clusterList1, const pandora::ClusterList &clusterList2)
Get closest distance between clusters in a pair of cluster lists.