LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
MergeClusterAlg.cxx
Go to the documentation of this file.
1 // Merge Cluster algorithm
3 //
4 // Runs on the output of previous clustering algorithms to merge
5 // clusters together which lie on a straight line and are within
6 // some separation threshold.
7 // Runs recursively over all clusters, including new ones formed
8 // in the algorithm.
9 //
10 // M Wallbank (m.wallbank@sheffield.ac.uk), July 2015
12 
14 
16  this->reconfigure(pset);
17  fTree = tfs->make<TTree>("MatchingVariables","MatchingVariables");
18  fTree->Branch("Angle",&fAngle);
19  fTree->Branch("Eigenvalue",&fEigenvalue);
20  fTree->Branch("Cluster1Size",&fCluster1Size);
21  fTree->Branch("Cluster2Size",&fCluster2Size);
22  fTree->Branch("Length1",&fLength1);
23  fTree->Branch("Length2",&fLength2);
24  fTree->Branch("Separation",&fSeparation);
25  fTree->Branch("CrossingDistance",&fCrossingDistance);
26  fTree->Branch("ProjectedWidth",&fProjectedWidth);
27  fTree->Branch("Overlap",&fOverlap);
28  fTree->Branch("TrueMerge",&fTrueMerge);
29 }
30 
31 void cluster::MergeClusterAlg::FindClusterEndPoints(art::PtrVector<recob::Hit> const& cluster, TVector2 const& centre, TVector2 const& direction, TVector2& start, TVector2& end) {
32 
34 
35  TVector2 pos;
36  std::map<double,TVector2> hitProjection;
37 
38  // Project all hits onto line to determine end points
39  for (auto &hit : cluster) {
40  pos = HitCoordinates(hit) - centre;
41  hitProjection[direction*pos] = pos;
42  }
43 
44  // Project end points onto line which passes through centre of cluster
45  start = hitProjection.begin()->second.Proj(direction) + centre;
46  end = hitProjection.rbegin()->second.Proj(direction) + centre;
47 
48  return;
49 
50 }
51 
52 double cluster::MergeClusterAlg::FindClusterOverlap(TVector2 const& direction, TVector2 const& centre, TVector2 const& start1, TVector2 const& end1, TVector2 const& start2, TVector2 const& end2) {
53 
55 
56  double clusterOverlap = 0;
57 
58  // Project onto the average direction through both clusters
59  double s1 = (start1-centre)*direction;
60  double e1 = (end1-centre)*direction;
61  double s2 = (start2-centre)*direction;
62  double e2 = (end2-centre)*direction;
63 
64  // Make sure end > start
65  if (s1 > e1) {
66  std::cout << "s1>e1: " << s1 << " and " << e1 << std::endl;
67  double tmp = e1;
68  e1 = s1;
69  s1 = tmp;
70  }
71  if (s2 > e2) {
72  std::cout << "s1>e1: " << s1 << " and " << e1 << std::endl;
73  double tmp = e2;
74  e2 = s2;
75  s2 = tmp;
76  }
77 
78  // Find the overlap of the clusters on the centre line
79  if ((e1 > s2) && (e2 > s1))
80  clusterOverlap = std::min((e1 - s2), (e2 - s1));
81 
82  return clusterOverlap;
83 
84 }
85 
86 double cluster::MergeClusterAlg::FindCrossingDistance(TVector2 const &direction1, TVector2 const &centre1, TVector2 const &direction2, TVector2 const &centre2) {
87 
89 
90  // Find intersection point of two lines drawn through the centre of the clusters
91  double dcross = (direction1.X() * direction2.Y()) - (direction1.Y() * direction2.X());
92  TVector2 p = centre2 - centre1;
93  double pcrossd = (p.X() * direction2.Y()) - (p.Y() * direction2.X());
94  TVector2 crossing = centre1 + ((pcrossd/dcross) * direction1);
95 
96  // Get distance from this point to the clusters
97  double crossingDistance = std::min((centre1-crossing).Mod(),(centre2-crossing).Mod());
98 
99  return crossingDistance;
100 
101 }
102 
104 
106 
107  double minDistance = 99999.;
108 
109  // Loop over the two clusters to find the smallest distance
110  for (auto const& hit1 : cluster1) {
111  for (auto const& hit2 : cluster2) {
112 
113  TVector2 pos1 = HitCoordinates(hit1);
114  TVector2 pos2 = HitCoordinates(hit2);
115 
116  double distance = (pos1 - pos2).Mod();
117 
118  if (distance < minDistance) minDistance = distance;
119 
120  }
121  }
122 
123  return minDistance;
124 
125 }
126 
127 double cluster::MergeClusterAlg::FindProjectedWidth(TVector2 const& centre1, TVector2 const& start1, TVector2 const& end1, TVector2 const& centre2, TVector2 const& start2, TVector2 const& end2) {
128 
130 
131  // Get the line running through the centre of the two clusters
132  TVector2 parallel = (centre2 - centre1).Unit();
133  TVector2 perpendicular = parallel.Rotate(TMath::Pi()/2);
134 
135  // Project the cluster vector onto this perpendicular line
136  double s1 = (start1-centre1)*perpendicular;
137  double e1 = (end1-centre1)*perpendicular;
138  double s2 = (start2-centre2)*perpendicular;
139  double e2 = (end2-centre2)*perpendicular;
140 
141  // Find the width in each direction
142  double projectionStart = std::max(TMath::Abs(s1), TMath::Abs(s2));
143  double projectionEnd = std::max(TMath::Abs(e1), TMath::Abs(e2));
144 
145  double projectionWidth = projectionStart + projectionEnd;
146 
147  return projectionWidth;
148 
149 }
150 
152 
154 
155  double wireCentre[3];
156  fGeom->WireIDToWireGeo(wireID).GetCenter(wireCentre);
157 
158  double globalWire;
159  if (fGeom->SignalType(wireID) == geo::kInduction) {
160  if (wireID.TPC % 2 == 0) globalWire = fGeom->WireCoordinate(wireCentre[1], wireCentre[2], wireID.Plane, 0, wireID.Cryostat);
161  else globalWire = fGeom->WireCoordinate(wireCentre[1], wireCentre[2], wireID.Plane, 1, wireID.Cryostat);
162  }
163  else {
164  if (wireID.TPC % 2 == 0) globalWire = wireID.Wire + ((wireID.TPC/2) * fGeom->Nwires(wireID.Plane, 0, wireID.Cryostat));
165  else globalWire = wireID.Wire + ((int)(wireID.TPC/2) * fGeom->Nwires(wireID.Plane, 1, wireID.Cryostat));
166  }
167 
168  return globalWire;
169 
170 }
171 
173 
175 
176  return TVector2(GlobalWire(hit->WireID()), hit->PeakTime());
177 
178 }
179 
181 
183 
184  // // ////// MAKE SOME MESSY CODE! CHECK FEATURES OF THESE CLUSTERS
185 
186  // // Truth matching
187  // for (unsigned int cluster = 0; cluster < planeClusters.size(); ++cluster) {
188  // std::map<int,double> trackMap;
189  // art::PtrVector<recob::Hit> hits = planeClusters.at(cluster);
190  // for (auto &hit : hits) {
191  // std::vector<sim::TrackIDE> ides = backtracker->HitToTrackID(hit);
192  // for (auto &ide : ides)
193  // trackMap[ide.trackID] += ide.energy;
194  // }
195  // // Find the true particle associated with this track
196  // double highEnergy = 0;
197  // int bestTrack = 0;
198  // for (auto &track : trackMap) {
199  // if (track.second > highEnergy) {
200  // highEnergy = track.second;
201  // bestTrack = track.first;
202  // }
203  // }
204  // trueClusterMap[cluster] = bestTrack;
205  // }
206 
207  // for (unsigned int cluster1It = 0; cluster1It < planeClusters.size(); ++cluster1It) {
208  // for (unsigned int cluster2It = cluster1It+1; cluster2It < planeClusters.size(); ++cluster2It) {
209 
210  // const art::PtrVector<recob::Hit> cluster1 = planeClusters.at(cluster1It);
211  // const art::PtrVector<recob::Hit> cluster2 = planeClusters.at(cluster2It);
212 
213  // // true merge
214  // if (trueClusterMap[cluster1It] == trueClusterMap[cluster2It])
215  // fTrueMerge = true;
216  // else fTrueMerge = false;
217 
218  // // geometry
219  // fCluster1Size = cluster1.size();
220  // fCluster2Size = cluster2.size();
221  // fSeparation = this->FindMinSeparation(cluster1, cluster2);
222 
223  // // PCA
224  // TPrincipal *pca = new TPrincipal(2,"");
225  // TPrincipal *pca1 = new TPrincipal(2,"");
226  // TPrincipal *pca2 = new TPrincipal(2,"");
227  // double hits[2];
228  // TVector2 pos;
229 
230  // // Cluster centre
231  // TVector2 chargePoint1 = TVector2(0,0), chargePoint2 = TVector2(0,0);
232  // double totalCharge1 = 0, totalCharge2 = 0;
233 
234  // for (auto &hit1 : cluster1) {
235  // pos = HitCoordinates(hit1);
236  // hits[0] = pos.X();
237  // hits[1] = pos.Y();
238  // pca->AddRow(hits);
239  // pca1->AddRow(hits);
240  // chargePoint1 += hit1->Integral() * pos;
241  // totalCharge1 += hit1->Integral();
242  // }
243  // for (auto &hit2 : cluster2) {
244  // pos = HitCoordinates(hit2);
245  // hits[0] = pos.X();
246  // hits[1] = pos.Y();
247  // pca->AddRow(hits);
248  // pca2->AddRow(hits);
249  // chargePoint2 += hit2->Integral() * pos;
250  // totalCharge2 += hit2->Integral();
251  // }
252 
253  // pca->MakePrincipals();
254  // pca1->MakePrincipals();
255  // pca2->MakePrincipals();
256 
257  // // Properties of these clusters
258  // TVector2 direction1 = TVector2( (*pca1->GetEigenVectors())[0][0], (*pca1->GetEigenVectors())[1][0] ).Unit();
259  // TVector2 direction2 = TVector2( (*pca2->GetEigenVectors())[0][0], (*pca2->GetEigenVectors())[1][0] ).Unit();
260  // TVector2 directionAv = ((direction1+direction2)/2).Unit();
261  // TVector2 centre1 = chargePoint1 / totalCharge1;
262  // TVector2 centre2 = chargePoint2 / totalCharge2;
263  // TVector2 centre = (centre1+centre2)/2;
264  // TVector2 start1, end1;
265  // TVector2 start2, end2;
266  // FindClusterEndPoints(cluster1, centre1, direction1, start1, end1);
267  // FindClusterEndPoints(cluster2, centre2, direction2, start2, end2);
268  // fLength1 = (end1-start1).Mod();
269  // fLength2 = (end2-start2).Mod();
270 
271  // // Properties of the pair of clusters
272  // fCrossingDistance = FindCrossingDistance(direction1, centre1, direction2, centre2);
273  // fProjectedWidth = FindProjectedWidth(centre1, start1, end1, centre2, start2, end2);
274  // fAngle = direction1.DeltaPhi(direction2);
275  // if (fAngle > 1.57) fAngle = 3.14159 - fAngle;
276  // fOverlap = FindClusterOverlap(directionAv, centre, start1, end1, start2, end2);
277  // fSeparation = FindMinSeparation(cluster1, cluster2);
278  // fEigenvalue = (*pca->GetEigenValues())[0];
279 
280  // fTree->Fill();
281 
282  // // std::cout << std::endl << "Plane " << fPlane << ": Clusters " << cluster1It << " and " << cluster2It << " have overlap " << fOverlap << " and start and end ... " << std::endl;
283  // // start1.Print();
284  // // end1.Print();
285  // // start2.Print();
286  // // end2.Print();
287 
288  // // // Find if this is merged!
289  // // if (fCrossingDistance < 6 + (5 / (fAngle - 0.05)))
290  // // fMerge = true;
291  // // else fMerge = false;
292 
293  // // if (fCluster1Size >= 10 && fCluster2Size >= 10) std::cout << "Merge " << fMerge << " and true merge " << fTrueMerge << std::endl;
294 
295  // }
296  // }
297 
298  // ----------------------------- END OF MESSY CODE! --------------------------------------------------------------------------------------------------------------
299 
300  std::vector<unsigned int> mergedClusters;
301 
302  std::vector<art::PtrVector<recob::Hit> > oldClusters = planeClusters;
303 
304  // Sort the clusters by size
305  std::sort(oldClusters.begin(), oldClusters.end(), [](const art::PtrVector<recob::Hit> &a, const art::PtrVector<recob::Hit> &b) {return a.size() > b.size();} );
306 
307  // Find the numbers of clusters above size threshold
308  unsigned int nclusters = 0;
309  for (auto &cluster : oldClusters)
310  if (cluster.size() >= fMinMergeClusterSize) ++nclusters;
311 
312  // Until all clusters are merged, create new clusters
313  bool mergedAllClusters = false;
314  while (!mergedAllClusters) {
315 
316  // New cluster
318 
319  // Put the largest unmerged cluster in this new cluster
320  for (unsigned int initCluster = 0; initCluster < oldClusters.size(); ++initCluster) {
321  if (oldClusters.at(initCluster).size() < fMinMergeClusterSize or std::find(mergedClusters.begin(), mergedClusters.end(), initCluster) != mergedClusters.end()) continue;
322  cluster = oldClusters.at(initCluster);
323  mergedClusters.push_back(initCluster);
324  break;
325  }
326 
327  // Merge all aligned clusters to this
328  bool mergedAllToThisCluster = false;
329  while (!mergedAllToThisCluster) {
330 
331  // Look at all clusters and merge
332  int nadded = 0;
333  for (unsigned int trialCluster = 0; trialCluster < oldClusters.size(); ++trialCluster) {
334 
335  if (oldClusters.at(trialCluster).size() < fMinMergeClusterSize or std::find(mergedClusters.begin(), mergedClusters.end(), trialCluster) != mergedClusters.end()) continue;
336 
337  // Calculate the PCA for each
338  TPrincipal *pca1 = new TPrincipal(2,""), *pca2 = new TPrincipal(2,"");
339  double hits[2];
340  TVector2 pos;
341 
342  // Cluster centre
343  TVector2 chargePoint1 = TVector2(0,0), chargePoint2 = TVector2(0,0);
344  double totalCharge1 = 0, totalCharge2 = 0;
345 
346  for (auto &hit1 : cluster) {
347  pos = HitCoordinates(hit1);
348  hits[0] = pos.X();
349  hits[1] = pos.Y();
350  pca1->AddRow(hits);
351  chargePoint1 += hit1->Integral() * pos;
352  totalCharge1 += hit1->Integral();
353  }
354  for (auto &hit2 : oldClusters.at(trialCluster)) {
355  pos = HitCoordinates(hit2);
356  hits[0] = pos.X();
357  hits[1] = pos.Y();
358  pca2->AddRow(hits);
359  chargePoint2 += hit2->Integral() * pos;
360  totalCharge2 += hit2->Integral();
361  }
362 
363  pca1->MakePrincipals();
364  pca2->MakePrincipals();
365 
366  // Properties of these clusters
367  TVector2 direction1 = TVector2( (*pca1->GetEigenVectors())[0][0], (*pca1->GetEigenVectors())[1][0] ).Unit();
368  TVector2 direction2 = TVector2( (*pca2->GetEigenVectors())[0][0], (*pca2->GetEigenVectors())[1][0] ).Unit();
369  TVector2 direction = ((direction1+direction2)/2).Unit();
370  TVector2 centre1 = chargePoint1 / totalCharge1;
371  TVector2 centre2 = chargePoint2 / totalCharge2;
372  TVector2 centre = (centre1+centre2)/2;
373  TVector2 start1, end1;
374  TVector2 start2, end2;
375  FindClusterEndPoints(cluster, centre1, direction1, start1, end1);
376  FindClusterEndPoints(oldClusters.at(trialCluster), centre2, direction2, start2, end2);
377  double length1 = (end1-start1).Mod();
378  double length2 = (end2-start2).Mod();
379 
380  // Properties of the pair of clusters
381  double crossingDistance = FindCrossingDistance(direction1, centre1, direction2, centre2);
382  double projectedWidth = FindProjectedWidth(centre1, start1, end1, centre2, start2, end2);
383  double angle = direction1.DeltaPhi(direction2);
384  if (angle > 1.57) angle = 3.14159 - angle;
385  double overlap = FindClusterOverlap(direction, centre, start1, end1, start2, end2);
386  double separation = FindMinSeparation(cluster, oldClusters.at(trialCluster));
387 
388  if (separation > fMaxMergeSeparation)
389  continue;
390  if (PassCuts(angle, crossingDistance, projectedWidth, separation, overlap, TMath::Max(length1, length2))) {
391 
392  for (auto &hit : oldClusters.at(trialCluster))
393  cluster.push_back(hit);
394 
395  mergedClusters.push_back(trialCluster);
396  ++nadded;
397 
398  }
399 
400  delete pca1;
401  delete pca2;
402 
403  } // loop over clusters to add
404 
405  if (nadded == 0) mergedAllToThisCluster = true;
406 
407  } // while loop
408 
409  clusters.push_back(cluster);
410  if (mergedClusters.size() == nclusters) mergedAllClusters = true;
411 
412  }
413 
414  return clusters.size();
415 
416 }
417 
418 bool cluster::MergeClusterAlg::PassCuts(double const& angle, double const& crossingDistance, double const& projectedWidth, double const& separation, double const& overlap, double const& longLength) {
419 
421 
422  bool passCrossingDistanceAngle = false;
423  if (crossingDistance < (-2 + (5 / (1 * TMath::Abs(angle)) - 0) ) )
424  passCrossingDistanceAngle = true;
425 
426  bool passSeparationAngle = false;
427  if (separation < (200 * TMath::Abs(angle) + 40))
428  passSeparationAngle = true;
429 
430  bool passProjectedWidth = false;
431  if (((double)projectedWidth/(double)longLength) < fProjWidthThreshold)
432  passProjectedWidth = true;
433 
434  return passCrossingDistanceAngle and passSeparationAngle and passProjectedWidth;
435 
436 }
437 
439  fMinMergeClusterSize = p.get<int> ("MinMergeClusterSize");
440  fMaxMergeSeparation = p.get<double>("MaxMergeSeparation");
441  fProjWidthThreshold = p.get<double>("ProjWidthThreshold");
442 }
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
MergeClusterAlg(fhicl::ParameterSet const &pset)
art::ServiceHandle< art::TFileService > tfs
geo::WireID WireID() const
Initial tdc tick for hit.
Definition: Hit.h:234
int MergeClusters(std::vector< art::PtrVector< recob::Hit > > const &planeClusters, std::vector< art::PtrVector< recob::Hit > > &clusters)
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:313
Float_t tmp
Definition: plot.C:37
SigType_t SignalType(geo::PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
Cluster finding and building.
TVector2 HitCoordinates(art::Ptr< recob::Hit > const &hit)
unsigned int Nwires(unsigned int p, unsigned int tpc=0, unsigned int cstat=0) const
Returns the total number of wires in the specified plane.
void reconfigure(fhicl::ParameterSet const &p)
double FindProjectedWidth(TVector2 const &centre1, TVector2 const &start1, TVector2 const &end1, TVector2 const &centre2, TVector2 const &start2, TVector2 const &end2)
void FindClusterEndPoints(art::PtrVector< recob::Hit > const &cluster, TVector2 const &centre, TVector2 const &direction, TVector2 &start, TVector2 &end)
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
Int_t max
Definition: plot.C:27
void hits()
Definition: readHits.C:15
Signal from induction planes.
Definition: geo_types.h:92
T get(std::string const &key) const
Definition: ParameterSet.h:231
double FindMinSeparation(art::PtrVector< recob::Hit > const &cluster1, art::PtrVector< recob::Hit > const &cluster2)
reference at(size_type n)
Definition: PtrVector.h:365
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:258
double FindCrossingDistance(TVector2 const &direction1, TVector2 const &centre1, TVector2 const &direction2, TVector2 const &centre2)
size_type size() const
Definition: PtrVector.h:308
Detector simulation of raw signals on wires.
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:219
T * make(ARGS...args) const
Int_t min
Definition: plot.C:26
double GlobalWire(geo::WireID const &wireID)
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
void GetCenter(double *xyz, double localz=0.0) const
Fills the world coordinate of a point on the wire.
Definition: WireGeo.cxx:68
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
double FindClusterOverlap(TVector2 const &direction, TVector2 const &centre, TVector2 const &start1, TVector2 const &end1, TVector2 const &start2, TVector2 const &end2)
unsigned int fMinMergeClusterSize
bool PassCuts(double const &angle, double const &crossingDistance, double const &projectedWidth, double const &separation, double const &overlap, double const &longLength)
art::ServiceHandle< geo::Geometry > fGeom
WireGeo const & WireIDToWireGeo(geo::WireID const &wireid) const
Returns the specified wire.