LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
BlurredClusteringAlg.cxx
Go to the documentation of this file.
1 // Implementation of the Blurred Clustering algorithm
3 //
4 // Converts a hit map into a 2D image of the hits before convoling
5 // with a Gaussian function to introduce a weighted blurring.
6 // Clustering proceeds on this blurred image to create more
7 // complete clusters.
8 //
9 // M Wallbank (m.wallbank@sheffield.ac.uk), May 2015
11 
12 #include "cetlib/pow.h"
14 
15 #include <cassert>
16 #include <cmath>
17 
19  : fDebug{pset.get<bool>("Debug",false)}
20  , fDetector{pset.get<std::string>("Detector","dune35t")}
21  , fBlurWire{pset.get<int>("BlurWire")}
22  , fBlurTick{pset.get<int>("BlurTick")}
23  , fSigmaWire{pset.get<double>("SigmaWire")}
24  , fSigmaTick{pset.get<double>("SigmaTick")}
25  , fMaxTickWidthBlur{pset.get<int>("MaxTickWidthBlur")}
26  , fClusterWireDistance{pset.get<int>("ClusterWireDistance")}
27  , fClusterTickDistance{pset.get<int>("ClusterTickDistance")}
28  , fNeighboursThreshold{pset.get<unsigned int>("NeighboursThreshold")}
29  , fMinNeighbours{pset.get<unsigned int>("MinNeighbours")}
30  , fMinSize{pset.get<unsigned int>("MinSize")}
31  , fMinSeed{pset.get<double>("MinSeed")}
32  , fTimeThreshold{pset.get<double>("TimeThreshold")}
33  , fChargeThreshold{pset.get<double>("ChargeThreshold")}
34  , fKernelWidth{2 * fBlurWire + 1}
37  , fDetProp{lar::providerFrom<detinfo::DetectorPropertiesService>()}
38 {}
39 
41 {
42  if (fDebugCanvas) {
43  std::string closeName = fDebugPDFName;
44  closeName.append("]");
45  fDebugCanvas->Print(closeName.c_str());
46  delete fDebugCanvas;
47  }
48 }
49 
51 {
52  if (!fDebugCanvas) {
53 
54  // Create the grayscale palette for the Z axis
55  Double_t Red[2] = { 1.00, 0.00 };
56  Double_t Green[2] = { 1.00, 0.00 };
57  Double_t Blue[2] = { 1.00, 0.00 };
58  Double_t Length[2] = { 0.00, 1.00 };
59  TColor::CreateGradientColorTable(2, Length, Red, Green, Blue, 1000);
60  gStyle->SetOptStat(110000);
61 
62  // Decide what to call this PDF
63  std::ostringstream oss;
64  oss << "BlurredImages_Run" << run << "_Subrun" << subrun;
65  fDebugPDFName = oss.str();
66  fDebugCanvas = new TCanvas(fDebugPDFName.c_str(), "Image canvas", 1000, 500);
67  fDebugPDFName.append(".pdf");
68 
69  std::string openName = fDebugPDFName;
70  openName.append("[");
71  fDebugCanvas->Print(openName.c_str());
72  fDebugCanvas->Divide(2, 2);
73  fDebugCanvas->SetGrid();
74  }
75 
76  // Clear the pads on the canvas
77  for (int i = 1; i <= 4; ++i) {
78  fDebugCanvas->GetPad(i)->Clear();
79  }
80 
81  std::ostringstream oss;
82  oss << "Event " << event;
83  fDebugCanvas->cd(1);
84  TLatex l;
85  l.SetTextSize(0.15);
86  l.DrawLatex(0.1, 0.1, oss.str().c_str());
87  fDebugCanvas->Print(fDebugPDFName.c_str());
88 
89 }
90 
91 void
93  std::vector<std::vector<int>> const& allClusterBins,
94  std::vector<art::PtrVector<recob::Hit>>& clusters) const
95 {
96  // Loop through the clusters (each a vector of bins)
97  for (auto const& bins : allClusterBins) {
98  // Convert the clusters (vectors of bins) to hits in a vector of recob::Hits
99  art::PtrVector<recob::Hit> clusHits = ConvertBinsToRecobHits(image, bins);
100 
101  mf::LogInfo("BlurredClustering") << "Cluster made from " << bins.size() << " bins, of which " << clusHits.size() << " were real hits";
102 
103  // Make sure the clusters are above the minimum cluster size
104  if (clusHits.size() < fMinSize) {
105  mf::LogVerbatim("BlurredClustering") << "Cluster of size " << clusHits.size() << " not saved since it is smaller than the minimum cluster size, set to " << fMinSize;
106  continue;
107  }
108 
109  clusters.push_back(clusHits);
110  }
111 }
112 
113 std::vector<std::vector<double>>
115 {
116  // Define the size of this particular plane -- dynamically to avoid huge histograms
117  int lowerTick = fDetProp->ReadOutWindowSize(), upperTick{}, lowerWire = fGeom->MaxWires(), upperWire{};
118  for (auto const& hit : hits) {
119  int histWire = GlobalWire(hit->WireID());
120  if (hit->PeakTime() < lowerTick) lowerTick = hit->PeakTime();
121  if (hit->PeakTime() > upperTick) upperTick = hit->PeakTime();
122  if (histWire < lowerWire) lowerWire = histWire;
123  if (histWire > upperWire) upperWire = histWire;
124  }
125  fLowerTick = lowerTick-20;
126  fUpperTick = upperTick+20;
127  fLowerWire = lowerWire-20;
128  fUpperWire = upperWire+20;
129 
130  // Use a map to keep a track of the real hits and their wire/ticks
131  fHitMap.clear();
133 
134  // Create a 2D vector
135  std::vector<std::vector<double>> image(fUpperWire-fLowerWire, std::vector<double>(fUpperTick-fLowerTick));
136 
137  // Look through the hits
138  for (auto const& hit : hits) {
139  int const wire = GlobalWire(hit->WireID());
140  auto const tick = static_cast<int>(hit->PeakTime());
141  float const charge = hit->Integral();
142 
143  // Fill hit map and keep a note of all real hits for later
144  if (charge > image.at(wire-fLowerWire).at(tick-fLowerTick)) {
145  image.at(wire-fLowerWire).at(tick-fLowerTick) = charge;
146  fHitMap[wire-fLowerWire][tick-fLowerTick] = hit;
147  }
148  }
149 
150  // Keep a note of dead wires
151  fDeadWires = std::vector<bool>(fUpperWire-fLowerWire, false);
152  geo::PlaneID const planeID = hits.front()->WireID().planeID();
153 
154  for (int wire = fLowerWire; wire < fUpperWire; ++wire) {
155  raw::ChannelID_t const channel = fGeom->PlaneWireToChannel(planeID.Plane,wire,planeID.TPC,planeID.Cryostat);
156  fDeadWires[wire-fLowerWire] = !fChanStatus.IsGood(channel);
157  }
158 
159  return image;
160 }
161 
162 int
163 cluster::BlurredClusteringAlg::FindClusters(std::vector<std::vector<double>> const& blurred,
164  std::vector<std::vector<int>>& allcluster)
165 {
166  // Size of image in x and y
167  int const nbinsx = blurred.size();
168  int const nbinsy = blurred.at(0).size();
169  int const nbins = nbinsx * nbinsy;
170 
171  // Vectors to hold hit information
172  std::vector<bool> used(nbins);
173  std::vector<std::pair<double, int>> values;
174 
175  // Place the bin number and contents as a pair in the values vector
176  for (int xbin = 0; xbin < nbinsx; ++xbin) {
177  for (int ybin = 0; ybin < nbinsy; ++ybin) {
178  int const bin = ConvertWireTickToBin(blurred, xbin, ybin);
179  values.emplace_back(ConvertBinToCharge(blurred, bin), bin);
180  }
181  }
182 
183  // Sort the values into charge order
184  std::sort(values.rbegin(), values.rend());
185 
186  // Count the number of iterations of the cluster forming loop (== number of clusters)
187  int niter = 0;
188 
189  // Clustering loops
190  // First loop - considers highest charge hits in decreasing order, and puts them in a new cluster if they aren't already clustered (makes new cluster every iteration)
191  // Second loop - looks at the direct neighbours of this seed and clusters to this if above charge/time thresholds. Runs recursively over all hits in cluster (inc. new ones)
192  while (true) {
193 
194  // Start a new cluster each time loop is executed
195  std::vector<int> cluster;
196  std::vector<double> times;
197 
198  // Get the highest charge bin (go no further if below seed threshold)
199  if (double const blurred_binval = values[niter].first; blurred_binval < fMinSeed)
200  break;
201 
202  // Iterate through the bins from highest charge down
203  int const bin = values[niter++].second;
204 
205  // Put this bin in used if not already there
206  if (used[bin])
207  continue;
208  used[bin] = true;
209 
210  // Start a new cluster
211  cluster.push_back(bin);
212 
213  // Get the time of this hit
214  if (double const time = GetTimeOfBin(blurred, bin); time > 0)
215  times.push_back(time);
216 
217  // Now cluster neighbouring hits to this seed
218  while (true) {
219 
220  bool added_cluster{false};
221 
222  for (unsigned int clusBin = 0; clusBin < cluster.size(); ++clusBin) {
223 
224  // Get x and y values for bin (c++ returns a%b = a if a<b)
225  int const binx = cluster[clusBin] % nbinsx;
226  int const biny = ((cluster[clusBin] - binx) / nbinsx) % nbinsy;
227 
228  // Look for hits in the neighbouring x/y bins
229  for (int x = binx - fClusterWireDistance; x <= binx + fClusterWireDistance; x++) {
230  if (x >= nbinsx or x < 0) continue;
231  for (int y = biny - fClusterTickDistance; y <= biny + fClusterTickDistance; y++) {
232  if (y >= nbinsy or y < 0) continue;
233  if (x == binx and y == biny) continue;
234 
235  // Get this bin
236  auto const bin = ConvertWireTickToBin(blurred, x, y);
237  if (bin >= nbinsx * nbinsy or bin < 0)
238  continue;
239  if (used[bin])
240  continue;
241 
242  // Get the blurred value and time for this bin
243  double const blurred_binval = ConvertBinToCharge(blurred, bin);
244  double const time = GetTimeOfBin(blurred, bin); // NB for 'fake' hits, time is defaulted to -10000
245 
246  // Check real hits pass time cut (ignores fake hits)
247  if (time > 0 && times.size() > 0 && ! PassesTimeCut(times, time))
248  continue;
249 
250  // Add to cluster if bin value is above threshold
251  if (blurred_binval > fChargeThreshold) {
252  used[bin] = true;
253  cluster.push_back(bin);
254  added_cluster = true;
255  if (time > 0) {
256  times.push_back(time);
257  }
258  } // End of adding blurred bin to cluster
259 
260  }
261  } // End of looking at directly neighbouring bins
262 
263  } // End of looping over bins already in this cluster
264 
265  if (!added_cluster)
266  break;
267 
268  } // End of adding hits to this cluster
269 
270  // Check this cluster is above minimum size
271  if (cluster.size() < fMinSize) {
272  for (auto const bin : cluster) {
273  assert(bin >= 0);
274  used[bin] = false;
275  }
276  continue;
277  }
278 
279  // Fill in holes in the cluster
280  for (unsigned int clusBin = 0; clusBin < cluster.size(); clusBin++) {
281 
282  // Looks at directly neighbouring bins (and not itself)
283  for (int x = -1; x <= 1; ++x) {
284  for (int y = -1; y <= 1; ++y) {
285  if (x == 0 && y == 0) continue;
286 
287  // Look at neighbouring bins to the clustered bin which are inside the cluster
288  int neighbouringBin = cluster[clusBin] + x + (y * nbinsx);
289  if (neighbouringBin < nbinsx || neighbouringBin % nbinsx == 0 || neighbouringBin % nbinsx == nbinsx - 1 || neighbouringBin >= nbinsx * (nbinsy - 1))
290  continue;
291 
292  double const time = GetTimeOfBin(blurred, neighbouringBin);
293 
294  // If not already clustered and passes neighbour/time thresholds, add to cluster
295  if (!used[neighbouringBin] && (NumNeighbours(nbinsx, used, neighbouringBin) > fNeighboursThreshold) && PassesTimeCut(times, time)) {
296  used[neighbouringBin] = true;
297  cluster.push_back(neighbouringBin);
298 
299  if (time > 0) {
300  times.push_back(time);
301  }
302  } // End of clustering neighbouring bin
303 
304  }
305  } // End of looping over neighbouring bins
306 
307  } // End of looping over bins already in cluster
308 
309  mf::LogVerbatim("Blurred Clustering") << "Size of cluster after filling in holes: " << cluster.size();
310 
311 
312  // Remove peninsulas - hits which have too few neighbouring hits in the cluster (defined by fMinNeighbours)
313  while (true) {
314  bool removed_cluster{false};
315 
316  // Loop over all the bins in the cluster
317  for (int clusBin = cluster.size() - 1; clusBin >= 0; clusBin--) {
318  auto const bin = cluster[clusBin];
319 
320  // If bin is in cluster ignore
321  if (bin < nbinsx || bin % nbinsx == 0 || bin % nbinsx == nbinsx - 1 || bin >= nbinsx * (nbinsy - 1)) continue;
322 
323  // Remove hit if it has too few neighbouring hits
324  if (NumNeighbours(nbinsx, used, bin) < fMinNeighbours) {
325  used[bin] = false;
326  removed_cluster = true;
327  cluster.erase(cluster.begin() + clusBin);
328  }
329  }
330 
331  if (!removed_cluster)
332  break;
333  }
334 
335  mf::LogVerbatim("Blurred Clustering") << "Size of cluster after removing peninsulas: " << cluster.size();
336 
337 
338  // Disregard cluster if not of minimum size
339  if (cluster.size() < fMinSize) {
340  for (auto const bin : cluster) {
341  assert(bin >= 0);
342  used[bin] = false;
343  }
344  continue;
345  }
346 
347  // Put this cluster in the vector of clusters
348  allcluster.push_back(cluster);
349 
350  } // End loop over this cluster
351 
352  // Return the number of clusters found in this hit map
353  return allcluster.size();
354 
355 }
356 
357 int
359 {
360  double globalWire = -999;
361 
362  // Induction
363  if (fGeom->SignalType(wireID) == geo::kInduction) {
364  double wireCentre[3];
365  fGeom->WireIDToWireGeo(wireID).GetCenter(wireCentre);
366  if (wireID.TPC % 2 == 0) globalWire = fGeom->WireCoordinate(wireCentre[1], wireCentre[2], wireID.Plane, 0, wireID.Cryostat);
367  else globalWire = fGeom->WireCoordinate(wireCentre[1], wireCentre[2], wireID.Plane, 1, wireID.Cryostat);
368  }
369 
370  // Collection
371  else {
372  // FOR COLLECTION WIRES, HARD CODE THE GEOMETRY FOR GIVEN DETECTORS
373  // THIS _SHOULD_ BE TEMPORARY. GLOBAL WIRE SUPPORT IS BEING ADDED TO THE LARSOFT GEOMETRY AND SHOULD BE AVAILABLE SOON
374  if (fDetector == "dune35t") {
375  unsigned int nwires = fGeom->Nwires(wireID.Plane, 0, wireID.Cryostat);
376  if (wireID.TPC == 0 or wireID.TPC == 1) globalWire = wireID.Wire;
377  else if (wireID.TPC == 2 or wireID.TPC == 3 or wireID.TPC == 4 or wireID.TPC == 5) globalWire = nwires + wireID.Wire;
378  else if (wireID.TPC == 6 or wireID.TPC == 7) globalWire = (2*nwires) + wireID.Wire;
379  else mf::LogError("BlurredClusterAlg") << "Error when trying to find a global induction plane coordinate for TPC " << wireID.TPC << " (geometry " << fDetector << ")";
380  }
381  else if (fDetector == "dune10kt") {
382  unsigned int nwires = fGeom->Nwires(wireID.Plane, 0, wireID.Cryostat);
383  // Detector geometry has four TPCs, two on top of each other, repeated along z...
384  int block = wireID.TPC / 4;
385  globalWire = (nwires*block) + wireID.Wire;
386  }
387  else {
388  double wireCentre[3];
389  fGeom->WireIDToWireGeo(wireID).GetCenter(wireCentre);
390  if (wireID.TPC % 2 == 0) globalWire = fGeom->WireCoordinate(wireCentre[1], wireCentre[2], wireID.Plane, 0, wireID.Cryostat);
391  else globalWire = fGeom->WireCoordinate(wireCentre[1], wireCentre[2], wireID.Plane, 1, wireID.Cryostat);
392  }
393  }
394 
395  return std::round(globalWire);
396 
397 }
398 
399 std::vector<std::vector<double>>
401 {
402  if (fSigmaWire == 0 and fSigmaTick == 0)
403  return image;
404 
405  auto const [blur_wire, blur_tick, sigma_wire, sigma_tick] = FindBlurringParameters();
406 
407  // Convolve the Gaussian
408  int width = 2 * blur_wire + 1;
409  int height = 2 * blur_tick + 1;
410  int nbinsx = image.size();
411  int nbinsy = image.at(0).size();
412 
413  // Blurred histogram and normalisation for each bin
414  std::vector<std::vector<double>> copy(nbinsx, std::vector<double>(nbinsy, 0));
415 
416  // Loop through all the bins in the histogram to blur
417  for (int x = 0; x < nbinsx; ++x) {
418  for (int y = 0; y < nbinsy; ++y) {
419 
420  if (image[x][y] == 0)
421  continue;
422 
423  // Scale the tick blurring based on the width of the hit
424  int tick_scale = std::sqrt(cet::square(fHitMap[x][y]->RMS()) + cet::square(sigma_tick)) / (double)sigma_tick;
425  tick_scale = std::max(std::min(tick_scale, fMaxTickWidthBlur), 1);
426  auto const& correct_kernel = fAllKernels[sigma_wire][sigma_tick*tick_scale];
427 
428  // Find any dead wires in the potential blurring region
429  auto const [lower_bin_dead, upper_bin_dead] = DeadWireCount(x, width);
430 
431  // Note of how many dead wires we have passed whilst blurring in the wire direction
432  // If blurring below the seed hit, need to keep a note of how many dead wires to come
433  // If blurring above, need to keep a note of how many dead wires have passed
434  auto dead_wires_passed{lower_bin_dead};
435 
436  // Loop over the blurring region around this hit
437  for (int blurx = -(width/2+lower_bin_dead); blurx < (width+1)/2+upper_bin_dead; ++blurx) {
438  if (x + blurx < 0) continue;
439  for (int blury = -height/2*tick_scale; blury < ((((height+1)/2)-1)*tick_scale)+1; ++blury) {
440  if (blurx < 0 and fDeadWires[x+blurx])
441  dead_wires_passed -= 1;
442 
443  // Smear the charge of this hit
444  double const weight = correct_kernel[fKernelWidth * (fKernelHeight / 2 + blury) + (fKernelWidth / 2 + (blurx - dead_wires_passed))];
445  if (x + blurx >= 0 and x + blurx < nbinsx and y + blury >= 0 and y + blury < nbinsy)
446  copy[x+blurx][y+blury] += weight * image[x][y];
447 
448  if (blurx > 0 and fDeadWires[x+blurx])
449  dead_wires_passed += 1;
450  }
451  } // blurring region
452 
453  }
454  } // hits to blur
455 
456  // HAVE REMOVED NOMALISATION CODE
457  // WHEN USING DIFFERENT KERNELS, THERE'S NO EASY WAY OF DOING THIS...
458  // RECONSIDER...
459 
460  // Return the blurred histogram
461  return copy;
462 }
463 
464 TH2F*
466  TString const name)
467 {
468  auto hist = new TH2F(name, name,
471  hist->SetXTitle("Wire number");
472  hist->SetYTitle("Tick number");
473  hist->SetZTitle("Charge");
474 
475  for (unsigned int imageWireIt = 0; imageWireIt < image.size(); ++imageWireIt) {
476  int const wire = imageWireIt + fLowerWire;
477  for (unsigned int imageTickIt = 0; imageTickIt < image.at(imageWireIt).size(); ++imageTickIt) {
478  int const tick = imageTickIt + fLowerTick;
479  hist->Fill(wire, tick, image.at(imageWireIt).at(imageTickIt));
480  }
481  }
482 
483  return hist;
484 }
485 
486 void
488  std::vector<art::PtrVector<recob::Hit>> const& allClusters,
489  int const pad,
490  int const tpc,
491  int const plane)
492 {
493  // Make a vector of clusters
494  std::vector<std::vector<int>> allClusterBins;
495 
496  for (auto const& cluster : allClusters) {
497  if (cluster.empty())
498  continue;
499 
500  std::vector<int> clusterBins;
501 
502  for (auto const& hit : cluster) {
503  unsigned int const wire = GlobalWire(hit->WireID());
504  float const tick = hit->PeakTime();
505  int bin = image->GetBin((wire-fLowerWire)+1,(tick-fLowerTick)+1);
506  if (cluster.size() < fMinSize)
507  bin *= -1;
508 
509  clusterBins.push_back(bin);
510  }
511 
512  allClusterBins.push_back(clusterBins);
513  }
514 
515  SaveImage(image, allClusterBins, pad, tpc, plane);
516 }
517 
518 void
520  int const pad,
521  int const tpc,
522  int const plane)
523 {
524  std::vector<std::vector<int>> allClusterBins;
525  SaveImage(image, allClusterBins, pad, tpc, plane);
526 }
527 
528 void
530  std::vector<std::vector<int>> const& allClusterBins,
531  int const pad,
532  int const tpc,
533  int const plane)
534 {
535  fDebugCanvas->cd(pad);
536  std::string stage;
537 
538  switch (pad) {
539  case 1:
540  stage = "Stage 1: Unblurred";
541  break;
542  case 2:
543  stage = "Stage 2: Blurred";
544  break;
545  case 3:
546  stage = "Stage 3: Blurred with clusters overlaid";
547  break;
548  case 4:
549  stage = "Stage 4: Output clusters";
550  break;
551  default:
552  stage = "Unknown stage";
553  break;
554  }
555 
556  std::stringstream title;
557  title << stage << " -- TPC " << tpc << ", Plane " << plane;// << " (Event " << fEvent << ")";
558 
559  image->SetName(title.str().c_str());
560  image->SetTitle(title.str().c_str());
561  image->DrawCopy("colz");
562 
563  // Draw the clustered hits on the histograms
564  int clusterNum = 2;
565  for (auto const& bins : allClusterBins) {
566  TMarker mark(0, 0, 20);
567  mark.SetMarkerColor(clusterNum);
568  mark.SetMarkerSize(0.1);
569 
570  for (auto bin : bins) {
571  // Hit from a cluster that we aren't going to save
572  if (bin < 0) {
573  bin *= -1;
574  mark.SetMarkerStyle(24);
575  }
576 
577  int wire, tick, z;
578  image->GetBinXYZ(bin,wire,tick,z);
579  mark.DrawMarker(wire+fLowerWire-1, tick+fLowerTick-1);
580  mark.SetMarkerStyle(20);
581  }
582  }
583 
584  if (pad == 4) {
585  fDebugCanvas->Print(fDebugPDFName.c_str());
586  fDebugCanvas->Clear("D");
587  }
588 
589 }
590 
591 // Private member functions
592 
595  std::vector<int> const& bins) const
596 {
597  // Create the vector of hits to output
599 
600  // Look through the hits in the cluster
601  for (auto const bin : bins) {
602  // Take each hit and convert it to a recob::Hit
604 
605  // If this hit was a real hit put it in the hit selection
606  if (!hit.isNull())
607  hits.push_back(hit);
608  }
609 
610  // Return the vector of hits to make cluster
611  return hits;
612 }
613 
616  int const bin) const
617 {
618  int const wire = bin % image.size();
619  int const tick = bin / image.size();
620  return fHitMap[wire][tick];
621 }
622 
623 int
625  int const xbin,
626  int const ybin) const
627 {
628  return ybin * image.size() + xbin;
629 }
630 
631 double
633  int const bin) const
634 {
635  int const x = bin % image.size();
636  int const y = bin / image.size();
637  return image.at(x).at(y);
638 }
639 
640 std::pair<int, int>
641 cluster::BlurredClusteringAlg::DeadWireCount(int const wire_bin, int const width) const
642 {
643  auto deadWires = std::make_pair(0, 0);
644 
645  int const lower_bin = width / 2;
646  int const upper_bin = (width+1) / 2;
647 
648  auto const offset = wire_bin + fLowerWire;
649  for (int wire = std::max(offset - lower_bin, fLowerWire); wire < std::min(offset + upper_bin, fUpperWire); ++wire) {
650  if (!fDeadWires[wire-fLowerWire]) continue;
651 
652  if (wire < offset)
653  ++deadWires.first;
654  else if (wire > offset)
655  ++deadWires.second;
656  }
657 
658  return deadWires;
659 
660 }
661 
662 std::array<int, 4>
664 {
665  // Calculate least squares slope
666  double nhits{}, sumx{}, sumy{}, sumx2{}, sumxy{};
667  for (unsigned int wireIt = 0; wireIt < fHitMap.size(); ++wireIt) {
668  for (unsigned int tickIt = 0; tickIt < fHitMap.at(wireIt).size(); ++tickIt) {
669  if (fHitMap[wireIt][tickIt].isNull())
670  continue;
671  ++nhits;
672  int const x = wireIt + fLowerWire;
673  int const y = tickIt + fLowerTick;
674  sumx += x;
675  sumy += y;
676  sumx2 += x*x;
677  sumxy += x*y;
678  }
679  }
680  double const gradient = (nhits * sumxy - sumx * sumy) / (nhits * sumx2 - sumx * sumx);
681 
682  // Get the rough unit vector for the trajectories, making sure to
683  // catch the vertical gradient.
684  auto const unit = std::isnan(gradient) ? TVector2{0, 1} : TVector2{1, gradient}.Unit();
685 
686  // Use this direction to scale the blurring radii and Gaussian sigma
687  int const blur_wire = std::max(std::abs(std::round(fBlurWire * unit.X())), 1.);
688  int const blur_tick = std::max(std::abs(std::round(fBlurTick * unit.Y())), 1.);
689 
690  int const sigma_wire = std::max(std::abs(std::round(fSigmaWire * unit.X())), 1.);
691  int const sigma_tick = std::max(std::abs(std::round(fSigmaTick * unit.Y())), 1.);
692  return {{blur_wire, blur_tick, sigma_wire, sigma_tick}};
693 }
694 
695 double
697  int const bin) const
698 {
699  auto const hit = ConvertBinToRecobHit(image, bin);
700  return hit.isNull() ? -10000. : hit->PeakTime();
701 }
702 
703 std::vector<std::vector<std::vector<double>>>
705 {
706  // Kernel size is the largest possible given the hit width rescaling
707  std::vector<std::vector<std::vector<double>>> allKernels(fSigmaWire + 1,
708  std::vector<std::vector<double>>(fSigmaTick*fMaxTickWidthBlur+1, std::vector<double>(fKernelWidth*fKernelHeight)));
709 
710  // Ranges of kernels to make
711  // Complete range of sigmas possible after dynamic fixing and hit width convolution
712  for (int sigma_wire = 1; sigma_wire <= fSigmaWire; ++sigma_wire) {
713  for (int sigma_tick = 1; sigma_tick <= fSigmaTick*fMaxTickWidthBlur; ++sigma_tick) {
714 
715  // New kernel
716  std::vector<double> kernel(fKernelWidth*fKernelHeight,0);
717 
718  // Smear out according to the blur radii in each direction
719  for (int i = -fBlurWire; i <= fBlurWire; i++) {
720  for (int j = -fBlurTick*fMaxTickWidthBlur; j <= fBlurTick*fMaxTickWidthBlur; j++) {
721 
722  // Fill kernel
723  double const sig2i = 2. * sigma_wire * sigma_wire;
724  double const sig2j = 2. * sigma_tick * sigma_tick;
725 
726  int const key = (fKernelWidth * (j + fBlurTick*fMaxTickWidthBlur)) + (i + fBlurWire);
727  double const value = 1. / std::sqrt(sig2i * M_PI) * std::exp(-i * i / sig2i) * 1. / std::sqrt(sig2j * M_PI) * std::exp(-j * j / sig2j);
728  kernel.at(key) = value;
729 
730  }
731  } // End loop over blurring region
732 
733  allKernels[sigma_wire][sigma_tick] = move(kernel);
734  }
735  }
736  return allKernels;
737 }
738 
739 unsigned int
741  std::vector<bool> const& used,
742  int const bin) const
743 {
744  unsigned int neighbours = 0;
745 
746  // Loop over all directly neighbouring hits (not itself)
747  for (int x = -1; x <= 1; x++) {
748  for (int y = -1; y <= 1; y++) {
749  if (x == 0 && y == 0) continue;
750 
751  // Determine bin
752  int neighbouringBin = bin + x + (y * nbinsx);
753 
754  // If this bin is in the cluster, increase the neighbouring bin counter
755  if (used.at(neighbouringBin))
756  neighbours++;
757  }
758  }
759 
760  // Return the number of neighbours in the cluster of a particular hit
761  return neighbours;
762 }
763 
764 bool
765 cluster::BlurredClusteringAlg::PassesTimeCut(std::vector<double> const& times,
766  double const time) const
767 {
768  for (auto const t : times) {
769  if (std::abs(time - t) < fTimeThreshold) return true;
770  }
771  return false;
772 }
Float_t x
Definition: compare.C:6
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
BlurredClusteringAlg(fhicl::ParameterSet const &pset)
virtual unsigned int ReadOutWindowSize() const =0
std::vector< std::vector< art::Ptr< recob::Hit > > > fHitMap
std::vector< std::vector< std::vector< double > > > MakeKernels() const
Makes all the kernels which could be required given the tuned parameters.
double GetTimeOfBin(std::vector< std::vector< double >> const &image, int bin) const
Returns the hit time of a hit in a particular bin.
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
bool PassesTimeCut(std::vector< double > const &times, double time) const
Determine if a hit is within a time threshold of any other hits in a cluster.
Float_t y
Definition: compare.C:6
Double_t z
Definition: plot.C:279
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
unsigned int NumNeighbours(int nx, std::vector< bool > const &used, int bin) const
Determines the number of clustered neighbours of a hit.
std::vector< std::vector< double > > ConvertRecobHitsToVector(std::vector< art::Ptr< recob::Hit >> const &hits)
Takes hit map and returns a 2D vector representing wire and tick, filled with the charge...
std::array< int, 4 > FindBlurringParameters() const
Dynamically find the blurring radii and Gaussian sigma in each dimension.
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
unsigned int MaxWires() const
Returns the largest number of wires among all planes in this detector.
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:313
SigType_t SignalType(geo::PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
Cluster finding and building.
art::ServiceHandle< geo::Geometry > fGeom
int FindClusters(std::vector< std::vector< double >> const &image, std::vector< std::vector< int >> &allcluster)
Find clusters in the histogram.
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.
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
art::PtrVector< recob::Hit > ConvertBinsToRecobHits(std::vector< std::vector< double >> const &image, std::vector< int > const &bins) const
Converts a vector of bins into a hit selection - not all the hits in the bins vector are real hits...
void CreateDebugPDF(int run, int subrun, int event)
Create the PDF to save debug images.
std::pair< int, int > DeadWireCount(int wire_bin, int width) const
double ConvertBinToCharge(std::vector< std::vector< double >> const &image, int bin) const
Returns the charge stored in the global bin value.
Int_t max
Definition: plot.C:27
void hits()
Definition: readHits.C:15
void push_back(Ptr< U > const &p)
Definition: PtrVector.h:441
int ConvertWireTickToBin(std::vector< std::vector< double >> const &image, int xbin, int ybin) const
Converts an xbin and a ybin to a global bin number.
Signal from induction planes.
Definition: geo_types.h:92
TH2F * MakeHistogram(std::vector< std::vector< double >> const &image, TString name)
Converts a 2D vector in a histogram for the debug pdf.
T get(std::string const &key) const
Definition: ParameterSet.h:231
art::Ptr< recob::Hit > ConvertBinToRecobHit(std::vector< std::vector< double >> const &image, int bin) const
Converts a bin into a recob::Hit (not all of these bins correspond to recob::Hits - some are fake hit...
void SaveImage(TH2F *image, std::vector< art::PtrVector< recob::Hit >> const &allClusters, int pad, int tpc, int plane)
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:258
float bin[41]
Definition: plottest35.C:14
size_type size() const
Definition: PtrVector.h:308
Detector simulation of raw signals on wires.
void ConvertBinsToClusters(std::vector< std::vector< double >> const &image, std::vector< std::vector< int >> const &allClusterBins, std::vector< art::PtrVector< recob::Hit >> &clusters) const
Takes a vector of clusters (itself a vector of hits) and turns them into clusters using the initial h...
double weight
Definition: plottest35.C:25
raw::ChannelID_t PlaneWireToChannel(WireID const &wireid) const
Returns the ID of the TPC channel connected to the specified wire.
std::string value(boost::any const &)
TH2F * hist
Definition: plot.C:136
Int_t min
Definition: plot.C:26
std::vector< std::vector< double > > GaussianBlur(std::vector< std::vector< double >> const &image)
Applies Gaussian blur to image.
std::vector< std::vector< std::vector< double > > > fAllKernels
bool isNull() const
Definition: Ptr.h:328
void GetCenter(double *xyz, double localz=0.0) const
Fills the world coordinate of a point on the wire.
Definition: WireGeo.cxx:68
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:27
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
lariov::ChannelStatusProvider const & fChanStatus
detinfo::DetectorProperties const * fDetProp
Event finding and building.
WireGeo const & WireIDToWireGeo(geo::WireID const &wireid) const
Returns the specified wire.
int GlobalWire(geo::WireID const &wireID)
Find the global wire position.