LArSoft  v10_04_05
Liquid Argon Software toolkit - https://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 
15 
16 #include "TPrincipal.h"
17 #include "TTree.h"
18 
20  : fWireReadoutGeom{&art::ServiceHandle<geo::WireReadout const>()->Get()}
21 {
22  fMinMergeClusterSize = pset.get<int>("MinMergeClusterSize");
23  fMaxMergeSeparation = pset.get<double>("MaxMergeSeparation");
24  fProjWidthThreshold = pset.get<double>("ProjWidthThreshold");
25  fTree = tfs->make<TTree>("MatchingVariables", "MatchingVariables");
26  fTree->Branch("Angle", &fAngle);
27  fTree->Branch("Eigenvalue", &fEigenvalue);
28  fTree->Branch("Cluster1Size", &fCluster1Size);
29  fTree->Branch("Cluster2Size", &fCluster2Size);
30  fTree->Branch("Length1", &fLength1);
31  fTree->Branch("Length2", &fLength2);
32  fTree->Branch("Separation", &fSeparation);
33  fTree->Branch("CrossingDistance", &fCrossingDistance);
34  fTree->Branch("ProjectedWidth", &fProjectedWidth);
35  fTree->Branch("Overlap", &fOverlap);
36  fTree->Branch("TrueMerge", &fTrueMerge);
37 }
38 
40  TVector2 const& centre,
41  TVector2 const& direction,
42  TVector2& start,
43  TVector2& end) const
44 {
46 
47  std::map<double, TVector2> hitProjection;
48 
49  // Project all hits onto line to determine end points
50  for (auto& hit : cluster) {
51  TVector2 pos = HitCoordinates(hit) - centre;
52  hitProjection[direction * pos] = pos;
53  }
54 
55  // Project end points onto line which passes through centre of cluster
56  start = hitProjection.begin()->second.Proj(direction) + centre;
57  end = hitProjection.rbegin()->second.Proj(direction) + centre;
58 }
59 
60 double cluster::MergeClusterAlg::FindClusterOverlap(TVector2 const& direction,
61  TVector2 const& centre,
62  TVector2 const& start1,
63  TVector2 const& end1,
64  TVector2 const& start2,
65  TVector2 const& end2) const
66 {
68 
69  double clusterOverlap = 0;
70 
71  // Project onto the average direction through both clusters
72  double s1 = (start1 - centre) * direction;
73  double e1 = (end1 - centre) * direction;
74  double s2 = (start2 - centre) * direction;
75  double e2 = (end2 - centre) * direction;
76 
77  // Make sure end > start
78  if (s1 > e1) {
79  std::cout << "s1>e1: " << s1 << " and " << e1 << std::endl;
80  double tmp = e1;
81  e1 = s1;
82  s1 = tmp;
83  }
84  if (s2 > e2) {
85  std::cout << "s1>e1: " << s1 << " and " << e1 << std::endl;
86  double tmp = e2;
87  e2 = s2;
88  s2 = tmp;
89  }
90 
91  // Find the overlap of the clusters on the centre line
92  if ((e1 > s2) && (e2 > s1)) clusterOverlap = std::min((e1 - s2), (e2 - s1));
93 
94  return clusterOverlap;
95 }
96 
97 double cluster::MergeClusterAlg::FindCrossingDistance(TVector2 const& direction1,
98  TVector2 const& centre1,
99  TVector2 const& direction2,
100  TVector2 const& centre2) const
101 {
103 
104  // Find intersection point of two lines drawn through the centre of the clusters
105  double dcross = (direction1.X() * direction2.Y()) - (direction1.Y() * direction2.X());
106  TVector2 p = centre2 - centre1;
107  double pcrossd = (p.X() * direction2.Y()) - (p.Y() * direction2.X());
108  TVector2 crossing = centre1 + ((pcrossd / dcross) * direction1);
109 
110  // Get distance from this point to the clusters
111  return std::min((centre1 - crossing).Mod(), (centre2 - crossing).Mod());
112 }
113 
115  art::PtrVector<recob::Hit> const& cluster2) const
116 {
118 
119  double minDistance = 99999.;
120 
121  // Loop over the two clusters to find the smallest distance
122  for (auto const& hit1 : cluster1) {
123  for (auto const& hit2 : cluster2) {
124 
125  TVector2 pos1 = HitCoordinates(hit1);
126  TVector2 pos2 = HitCoordinates(hit2);
127 
128  double distance = (pos1 - pos2).Mod();
129 
130  if (distance < minDistance) minDistance = distance;
131  }
132  }
133 
134  return minDistance;
135 }
136 
137 double cluster::MergeClusterAlg::FindProjectedWidth(TVector2 const& centre1,
138  TVector2 const& start1,
139  TVector2 const& end1,
140  TVector2 const& centre2,
141  TVector2 const& start2,
142  TVector2 const& end2) const
143 {
145 
146  // Get the line running through the centre of the two clusters
147  TVector2 parallel = (centre2 - centre1).Unit();
148  TVector2 perpendicular = parallel.Rotate(TMath::Pi() / 2);
149 
150  // Project the cluster vector onto this perpendicular line
151  double s1 = (start1 - centre1) * perpendicular;
152  double e1 = (end1 - centre1) * perpendicular;
153  double s2 = (start2 - centre2) * perpendicular;
154  double e2 = (end2 - centre2) * perpendicular;
155 
156  // Find the width in each direction
157  double projectionStart = std::max(TMath::Abs(s1), TMath::Abs(s2));
158  double projectionEnd = std::max(TMath::Abs(e1), TMath::Abs(e2));
159 
160  return projectionStart + projectionEnd; // FIXME (KJK): Really? The width is the start + the end?
161 }
162 
164 {
166 
167  auto const wireCenter = fWireReadoutGeom->Wire(wireID).GetCenter();
168  geo::PlaneID const planeID{wireID.Cryostat, wireID.TPC % 2, wireID.Plane};
169 
170  if (fWireReadoutGeom->SignalType(wireID) == geo::kInduction) {
171  return fWireReadoutGeom->Plane(planeID).WireCoordinate(wireCenter);
172  }
173  return wireID.Wire + ((wireID.TPC / 2) * fWireReadoutGeom->Nwires(planeID));
174 }
175 
177 {
179  return TVector2(GlobalWire(hit->WireID()), hit->PeakTime());
180 }
181 
183  std::vector<art::PtrVector<recob::Hit>> const& planeClusters,
184  std::vector<art::PtrVector<recob::Hit>>& clusters) const
185 {
187 
188  std::vector<unsigned int> mergedClusters;
189 
190  std::vector<art::PtrVector<recob::Hit>> oldClusters = planeClusters;
191 
192  // Sort the clusters by size
193  std::sort(oldClusters.begin(),
194  oldClusters.end(),
196  return a.size() > b.size();
197  });
198 
199  // Find the numbers of clusters above size threshold
200  unsigned int nclusters = 0;
201  for (auto& cluster : oldClusters)
202  if (cluster.size() >= fMinMergeClusterSize) ++nclusters;
203 
204  // Until all clusters are merged, create new clusters
205  bool mergedAllClusters = false;
206  while (!mergedAllClusters) {
207 
208  // New cluster
210 
211  // Put the largest unmerged cluster in this new cluster
212  for (unsigned int initCluster = 0; initCluster < oldClusters.size(); ++initCluster) {
213  if (oldClusters.at(initCluster).size() < fMinMergeClusterSize or
214  std::find(mergedClusters.begin(), mergedClusters.end(), initCluster) !=
215  mergedClusters.end())
216  continue;
217  cluster = oldClusters.at(initCluster);
218  mergedClusters.push_back(initCluster);
219  break;
220  }
221 
222  // Merge all aligned clusters to this
223  bool mergedAllToThisCluster = false;
224  while (!mergedAllToThisCluster) {
225 
226  // Look at all clusters and merge
227  int nadded = 0;
228  for (unsigned int trialCluster = 0; trialCluster < oldClusters.size(); ++trialCluster) {
229 
230  if (oldClusters.at(trialCluster).size() < fMinMergeClusterSize or
231  std::find(mergedClusters.begin(), mergedClusters.end(), trialCluster) !=
232  mergedClusters.end())
233  continue;
234 
235  // Calculate the PCA for each
236  TPrincipal *pca1 = new TPrincipal(2, ""), *pca2 = new TPrincipal(2, "");
237  double hits[2];
238  TVector2 pos;
239 
240  // Cluster centre
241  TVector2 chargePoint1 = TVector2(0, 0), chargePoint2 = TVector2(0, 0);
242  double totalCharge1 = 0, totalCharge2 = 0;
243 
244  for (auto& hit1 : cluster) {
245  pos = HitCoordinates(hit1);
246  hits[0] = pos.X();
247  hits[1] = pos.Y();
248  pca1->AddRow(hits);
249  chargePoint1 += hit1->Integral() * pos;
250  totalCharge1 += hit1->Integral();
251  }
252  for (auto& hit2 : oldClusters.at(trialCluster)) {
253  pos = HitCoordinates(hit2);
254  hits[0] = pos.X();
255  hits[1] = pos.Y();
256  pca2->AddRow(hits);
257  chargePoint2 += hit2->Integral() * pos;
258  totalCharge2 += hit2->Integral();
259  }
260 
261  pca1->MakePrincipals();
262  pca2->MakePrincipals();
263 
264  // Properties of these clusters
265  TVector2 direction1 =
266  TVector2((*pca1->GetEigenVectors())[0][0], (*pca1->GetEigenVectors())[1][0]).Unit();
267  TVector2 direction2 =
268  TVector2((*pca2->GetEigenVectors())[0][0], (*pca2->GetEigenVectors())[1][0]).Unit();
269  TVector2 direction = ((direction1 + direction2) / 2).Unit();
270  TVector2 centre1 = chargePoint1 / totalCharge1;
271  TVector2 centre2 = chargePoint2 / totalCharge2;
272  TVector2 centre = (centre1 + centre2) / 2;
273  TVector2 start1, end1;
274  TVector2 start2, end2;
275  FindClusterEndPoints(cluster, centre1, direction1, start1, end1);
276  FindClusterEndPoints(oldClusters.at(trialCluster), centre2, direction2, start2, end2);
277  double length1 = (end1 - start1).Mod();
278  double length2 = (end2 - start2).Mod();
279 
280  // Properties of the pair of clusters
281  double crossingDistance = FindCrossingDistance(direction1, centre1, direction2, centre2);
282  double projectedWidth = FindProjectedWidth(centre1, start1, end1, centre2, start2, end2);
283  double angle = direction1.DeltaPhi(direction2);
284  if (angle > 1.57) angle = 3.14159 - angle;
285  double separation = FindMinSeparation(cluster, oldClusters.at(trialCluster));
286 
287  if (separation > fMaxMergeSeparation) continue;
288  if (PassCuts(
289  angle, crossingDistance, projectedWidth, separation, TMath::Max(length1, length2))) {
290 
291  for (auto& hit : oldClusters.at(trialCluster))
292  cluster.push_back(hit);
293 
294  mergedClusters.push_back(trialCluster);
295  ++nadded;
296  }
297 
298  delete pca1;
299  delete pca2;
300 
301  } // loop over clusters to add
302 
303  if (nadded == 0) mergedAllToThisCluster = true;
304 
305  } // while loop
306 
307  clusters.push_back(cluster);
308  if (mergedClusters.size() == nclusters) mergedAllClusters = true;
309  }
310 
311  return clusters.size();
312 }
313 
314 bool cluster::MergeClusterAlg::PassCuts(double const angle,
315  double const crossingDistance,
316  double const projectedWidth,
317  double const separation,
318  double const longLength) const
319 {
321 
322  bool passCrossingDistanceAngle = false;
323  if (crossingDistance < (-2 + (5 / (1 * TMath::Abs(angle)) - 0))) passCrossingDistanceAngle = true;
324 
325  bool passSeparationAngle = false;
326  if (separation < (200 * TMath::Abs(angle) + 40)) passSeparationAngle = true;
327 
328  bool passProjectedWidth = false;
329  if (((double)projectedWidth / (double)longLength) < fProjWidthThreshold)
330  passProjectedWidth = true;
331 
332  return passCrossingDistanceAngle and passSeparationAngle and passProjectedWidth;
333 }
MergeClusterAlg(fhicl::ParameterSet const &pset)
Point_t const & GetCenter() const
Returns the world coordinate of the center of the wire [cm].
Definition: WireGeo.h:219
int MergeClusters(std::vector< art::PtrVector< recob::Hit >> const &planeClusters, std::vector< art::PtrVector< recob::Hit >> &clusters) const
unsigned int Nwires(PlaneID const &planeid) const
Returns the total number of wires in the specified plane.
double WireCoordinate(Point_t const &point) const
Returns the coordinate of the point on the plane, in wire units.
Definition: PlaneGeo.h:704
The data type to uniquely identify a Plane.
Definition: geo_types.h:364
void FindClusterEndPoints(art::PtrVector< recob::Hit > const &cluster, TVector2 const &centre, TVector2 const &direction, TVector2 &start, TVector2 &end) const
art::ServiceHandle< art::TFileService const > tfs
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:195
cout<< "Opened file "<< fin<< " ixs= "<< ixs<< endl;if(ixs==0) hhh=(TH1F *) fff-> Get("h1")
Definition: AddMC.C:8
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:430
geo::WireID const & WireID() const
Initial tdc tick for hit.
Definition: Hit.h:290
Float_t tmp
Definition: plot.C:35
SigType_t SignalType(PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
Cluster finding and building.
geo::WireReadoutGeom const * fWireReadoutGeom
WireGeo const & Wire(WireID const &wireid) const
Returns the specified wire.
double FindClusterOverlap(TVector2 const &direction, TVector2 const &centre, TVector2 const &start1, TVector2 const &end1, TVector2 const &start2, TVector2 const &end2) const
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
void hits()
Definition: readHits.C:15
Signal from induction planes.
Definition: geo_types.h:147
reference at(size_type n)
Definition: PtrVector.h:359
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:373
size_type size() const
Definition: PtrVector.h:302
Detector simulation of raw signals on wires.
TVector2 HitCoordinates(art::Ptr< recob::Hit > const &hit) const
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:226
double FindMinSeparation(art::PtrVector< recob::Hit > const &cluster1, art::PtrVector< recob::Hit > const &cluster2) const
double FindCrossingDistance(TVector2 const &direction1, TVector2 const &centre1, TVector2 const &direction2, TVector2 const &centre2) const
double GlobalWire(geo::WireID const &wireID) const
PlaneGeo const & Plane(TPCID const &tpcid, View_t view) const
Returns the specified wire.
bool PassCuts(double angle, double crossingDistance, double projectedWidth, double separation, double longLength) const
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:315
double FindProjectedWidth(TVector2 const &centre1, TVector2 const &start1, TVector2 const &end1, TVector2 const &centre2, TVector2 const &start2, TVector2 const &end2) const
unsigned int fMinMergeClusterSize