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