LArSoft  v10_04_05
Liquid Argon Software toolkit - https://larsoft.org/
LineMerger_module.cc
Go to the documentation of this file.
1 //
3 // LineMerger class
4 //
5 // maddalena.antonello@lngs.infn.it
6 // ornella.palamara@lngs.infn.it
7 // biagio.rossi@lhep.unibe.ch
8 // msoderbe@syr.edu
9 // joshua.spitz@yale.edu
10 //
11 // This algorithm is designed to merge 2D lines with similar slope and endpoints
12 //
14 
15 #include <array>
16 #include <cmath> // std::abs(), std::sqrt()
17 #include <iomanip>
18 #include <memory> // std::unique_ptr<>
19 #include <string>
20 #include <utility> // std::move()
21 
22 //Framework includes:
29 #include "fhiclcpp/ParameterSet.h"
31 
32 //LArSoft includes:
45 
46 namespace cluster {
47 
48  class LineMerger : public art::EDProducer {
49  public:
50  explicit LineMerger(fhicl::ParameterSet const& pset);
51 
52  private:
53  void produce(art::Event& evt) override;
54 
55  std::string fClusterModuleLabel;
56  double fSlope; // tolerance for matching angles between two lines (in units of radians)
57  double fEndpointWindow; // tolerance for matching endpoints (in units of time samples)
58 
59  bool SlopeCompatibility(double slope1, double slope2);
60  int EndpointCompatibility(float sclstartwire,
61  float sclstarttime,
62  float sclendwire,
63  float sclendtime,
64  float cl2startwire,
65  float cl2starttime,
66  float cl2endwire,
67  float cl2endtime);
68 
69  }; // class LineMerger
70 
72  class ClusterMerger {
73  public:
74  // NOTE if you feel like copying this class, move it into its own header
75  // instead, and if you need it for a different hit or hit pointer, make it
76  // a template instead
78  using HitVector_t = std::vector<HitPtr_t>;
79 
82 
83  ClusterMerger() = default;
84 
85  ClusterMerger(recob::Cluster const& cluster) : ClusterMerger() { Add(cluster); }
86 
102  bool Add(recob::Cluster const& cluster);
103 
106 
108  float StartWire() const { return fEndWires[ClusterEnds_t::clStart]; }
109 
111  float StartTick() const { return fEndTicks[ClusterEnds_t::clStart]; }
112 
114  float SigmaStartWire() const { return fSigmaEndWires[ClusterEnds_t::clStart]; }
115 
116  // Returns the uncertainty on tick coordinate of the start of the cluster
117  float SigmaStartTick() const { return fSigmaEndTicks[ClusterEnds_t::clStart]; }
118 
120  float EndWire() const { return fEndWires[ClusterEnds_t::clEnd]; }
121 
123  float EndTick() const { return fEndTicks[ClusterEnds_t::clEnd]; }
124 
126  float SigmaEndWire() const { return fSigmaEndWires[ClusterEnds_t::clEnd]; }
127 
129  float SigmaEndTick() const { return fSigmaEndTicks[ClusterEnds_t::clEnd]; }
130 
132  float WireCoord(ClusterEnds_t side) const { return fEndWires[side]; }
133 
135  float TickCoord(ClusterEnds_t side) const { return fEndTicks[side]; }
136 
138  float SigmaWireCoord(ClusterEnds_t side) const { return fSigmaEndWires[side]; }
139 
141  float SigmaTickCoord(ClusterEnds_t side) const { return fSigmaEndTicks[side]; }
142 
144  float StartCharge() const { return fEndCharges[ClusterEnds_t::clStart]; }
145 
147  float StartAngle() const { return fAngles[ClusterEnds_t::clStart]; }
148 
150  float StartOpeningAngle() const { return fOpeningAngles[ClusterEnds_t::clStart]; }
151 
153  float EndCharge() const { return fEndCharges[ClusterEnds_t::clEnd]; }
154 
156  float EndAngle() const { return fAngles[ClusterEnds_t::clEnd]; }
157 
159  float EndOpeningAngle() const { return fOpeningAngles[ClusterEnds_t::clEnd]; }
160 
162  float EdgeCharge(ClusterEnds_t side) const { return fEndCharges[side]; }
163 
165  float Angle(ClusterEnds_t side) const { return fAngles[side]; }
166 
168  float OpeningAngle(ClusterEnds_t side) const { return fOpeningAngles[side]; }
169 
171  float Width() const { return fWidth; }
172 
174  geo::View_t View() const { return fView; }
175 
177  geo::PlaneID Plane() const { return fPlaneID; }
178 
180  bool hasPlane() const { return Plane().isValid; }
181 
182  protected:
184  float fEndWires[ClusterEnds_t::NEnds];
185 
187  float fSigmaEndWires[ClusterEnds_t::NEnds];
188 
190  float fEndTicks[ClusterEnds_t::NEnds];
191 
193  float fSigmaEndTicks[ClusterEnds_t::NEnds];
194 
196  float fEndCharges[ClusterEnds_t::NEnds];
197 
199  float fAngles[ClusterEnds_t::NEnds];
200 
202  float fOpeningAngles[ClusterEnds_t::NEnds];
203 
205  float fWidth;
206 
208 
210 
211  unsigned int n_clusters = 0;
212 
214  void AdoptEnd(recob::Cluster const& cluster, ClusterEnds_t iEnd);
215 
216  template <typename T>
217  static void top(T& var, T value)
218  {
219  if (value > var) var = value;
220  }
221  template <typename T>
222  static void bot(T& var, T value)
223  {
224  if (value < var) var = value;
225  }
226  }; // class ClusterMerger
227 
229  {
230  const ClusterEnds_t iDestEnd = iSrcEnd;
231  fEndWires[iDestEnd] = cluster.WireCoord(iSrcEnd);
232  fSigmaEndWires[iDestEnd] = cluster.SigmaWireCoord(iSrcEnd);
233  fEndTicks[iDestEnd] = cluster.TickCoord(iSrcEnd);
234  fSigmaEndTicks[iDestEnd] = cluster.SigmaTickCoord(iSrcEnd);
235  fEndCharges[iDestEnd] = cluster.EdgeCharge(iSrcEnd);
236  fAngles[iDestEnd] = cluster.Angle(iSrcEnd);
237  fOpeningAngles[iDestEnd] = cluster.OpeningAngle(iSrcEnd);
238  } // ClusterMerger::AdoptEnd()
239 
241  {
242  if (!cluster.isValid()) return false;
243 
244  if (n_clusters == 0) { // special case: we are still empty
245  AdoptEnd(cluster, ClusterEnds_t::clStart);
246  AdoptEnd(cluster, ClusterEnds_t::clEnd);
247  fWidth = cluster.Width();
248  fView = cluster.View();
249  fPlaneID = cluster.Plane();
250  ++n_clusters;
251  return true;
252  } // if empty
253 
254  if (cluster.View() != View()) return false;
255 
256  if (cluster.hasPlane() && hasPlane() && (cluster.Plane() != Plane())) return false;
257 
258  // this code has been moved here from the old recon::Cluster::operator+
259  // of recob::Cluster v13.
260  if (cluster.StartWire() < StartWire()) { // adopt the new start
261  AdoptEnd(cluster, ClusterEnds_t::clStart);
262  }
263  if (cluster.EndWire() < EndWire()) { // adopt the new end
264  AdoptEnd(cluster, ClusterEnds_t::clEnd);
265  }
266 
267  top(fWidth, cluster.Width()); // extend width
268 
269  if (!hasPlane()) fPlaneID = cluster.Plane();
270 
271  return true;
272  } // ClusterMerger::Add(Cluster)
273 
276  public:
277  // NOTE if you feel like copying this class, move it into its own header
278  // instead, and if you need it for a different hit or hit pointer, make it
279  // a template instead
281  using HitVector_t = std::vector<HitPtr_t>;
282 
283  ClusterAndHitMerger() = default;
284 
286  {
287  Add(cluster, cluster_hits);
288  }
289 
303  bool Add(recob::Cluster const& cluster, HitVector_t const& cluster_hits, bool prepend = false);
304 
307 
309  HitVector_t const& Hits() const { return hits; }
310 
312  unsigned int NHits() const { return hits.size(); }
313 
315 
316  protected:
318 
319  void AddHits(HitVector_t const& cluster_hits, bool prepend)
320  {
321  hits.insert(prepend ? hits.begin() : hits.end(), cluster_hits.begin(), cluster_hits.end());
322  } // AddHits()
323 
324  }; // class ClusterAndHitMerger
325 
327  HitVector_t const& cluster_hits,
328  bool prepend /* = false */
329  )
330  {
331  if (!ClusterMerger::Add(cluster)) return false;
332 
333  AddHits(cluster_hits, prepend);
334  return true;
335  } // ClusterAndHitMerger::Add()
336 
337  //-------------------------------------------------
339  : EDProducer{pset}
340  , fClusterModuleLabel(pset.get<std::string>("ClusterModuleLabel"))
341  , fSlope(pset.get<double>("Slope"))
342  , fEndpointWindow(pset.get<double>("EndpointWindow"))
343  {
344  produces<std::vector<recob::Cluster>>();
345  produces<art::Assns<recob::Cluster, recob::Hit>>();
346  }
347 
348  //------------------------------------------------------------------------------------//
350  {
351  // Get a Handle for the input Cluster object(s).
352  art::Handle<std::vector<recob::Cluster>> clusterVecHandle;
353  evt.getByLabel(fClusterModuleLabel, clusterVecHandle);
354 
355  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
356  auto const detProp =
358  util::GeometryUtilities const gser{*lar::providerFrom<geo::Geometry>(),
360  clockData,
361  detProp};
362 
363  constexpr size_t nViews = 3; // number of views we map
364 
365  //one vector for each view in the geometry (holds the index of the cluster)
366  std::array<std::vector<size_t>, nViews> ClsIndices;
367 
368  //vector with indicators for whether a cluster has been merged already
369  std::array<std::vector<int>, nViews> Cls_matches;
370 
371  // loop over the input Clusters
372  for (size_t i = 0; i < clusterVecHandle->size(); ++i) {
373 
374  //get a art::Ptr to each Cluster
375  art::Ptr<recob::Cluster> cl(clusterVecHandle, i);
376 
377  size_t view = 0;
378  switch (cl->View()) {
379  case geo::kU: view = 0; break;
380  case geo::kV: view = 1; break;
381  case geo::kZ: view = 2; break;
382  default: continue; // ignore this cluster and process the next one
383  } // end switch on view
384 
385  Cls_matches[view].push_back(0);
386  ClsIndices[view].push_back(i);
387  } // end loop over input clusters
388 
389  auto SuperClusters = std::make_unique<std::vector<recob::Cluster>>();
390  auto assn = std::make_unique<art::Assns<recob::Cluster, recob::Hit>>();
391 
392  // prepare the algorithm to compute the cluster characteristics;
393  // we use the "standard" one here; configuration would happen here,
394  // but we are using the default configuration for that algorithm
396 
397  art::FindManyP<recob::Hit> fmh(clusterVecHandle, evt, fClusterModuleLabel);
398 
399  for (size_t i = 0; i < nViews; ++i) {
400 
401  int clustersfound = 0; // how many merged clusters found in each plane
402  int clsnum1 = 0;
403 
404  for (size_t c = 0; c < ClsIndices[i].size(); ++c) {
405  if (Cls_matches[i][clsnum1] == 1) {
406  ++clsnum1;
407  continue;
408  }
409 
410  // make a new cluster to put into the SuperClusters collection
411  // because we want to be able to adjust it later;
412  // use the hits associated with the current cluster
413  recob::Cluster const& StartingCluster = clusterVecHandle->at(ClsIndices[i][c]);
414  ClusterAndHitMerger cl1(StartingCluster, fmh.at(ClsIndices[i][c]));
415  const recob::Cluster::ID_t clusterID = StartingCluster.ID();
416 
417  Cls_matches[i][clsnum1] = 1;
418  ++clustersfound;
419 
420  int clsnum2 = 0;
421  for (size_t c2 = 0; c2 < ClsIndices[i].size(); ++c2) {
422 
423  if (Cls_matches[i][clsnum2] == 1) {
424  ++clsnum2;
425  continue;
426  }
427 
428  const recob::Cluster& cl2(clusterVecHandle->at(ClsIndices[i][c2]));
429 
430  // check that the slopes are the same
431  // added 13.5 ticks/wirelength in ArgoNeuT.
432  // \todo need to make this detector agnostic
433  // would be nice to have a LArProperties function that returns ticks/wire.
434  bool sameSlope = SlopeCompatibility(cl1.StartAngle(), cl2.EndAngle()) ||
435  SlopeCompatibility(cl1.EndAngle(), cl2.StartAngle());
436 
437  // check that the endpoints fall within a circular window of each other
438  // done in place of intercept matching
439  int sameEndpoint = EndpointCompatibility(cl1.StartWire(),
440  cl1.StartTick(),
441  cl1.EndWire(),
442  cl1.EndTick(),
443  cl2.StartWire(),
444  cl2.StartTick(),
445  cl2.EndWire(),
446  cl2.EndTick());
447 
448  // if the slopes and end points are the same, combine the clusters
449  // note that after 1 combination cl1 is no longer what we started
450  // with
451  if (sameSlope && (sameEndpoint != 0)) {
452  // combine the hit collections too
453  // (find the hits associated with this second cluster);
454  // take into account order when merging hits from two clusters: doc-1776
455  // if sameEndpoint is 1, the new hits come first
456  cl1.Add(cl2, fmh.at(ClsIndices[i][c2]), sameEndpoint == 1);
457  Cls_matches[i][clsnum2] = 1;
458  }
459 
460  ++clsnum2;
461  } // end loop over second cluster iterator
462 
463  // now add the final version of cl1 to the collection of SuperClusters
464  // and create the association between the super cluster and the hits
465  ClusterParamAlgo.ImportHits(gser, cl1.Hits());
466 
467  // create the recob::Cluster directly in the vector
468  SuperClusters->emplace_back(cl1.StartWire(), // start_wire
469  cl1.SigmaStartWire(), // sigma_start_wire
470  cl1.StartTick(), // start_tick
471  cl1.SigmaStartTick(), // sigma_start_tick
472  cl1.StartCharge(), // start_charge
473  cl1.StartAngle(), // start_angle
474  cl1.StartOpeningAngle(), // start_opening
475  cl1.EndWire(), // end_wire
476  cl1.SigmaEndWire(), // sigma_end_wire
477  cl1.EndTick(), // end_time
478  cl1.SigmaEndTick(), // sigma_end_tick
479  cl1.EndCharge(), // end_charge
480  cl1.EndAngle(), // end_angle
481  cl1.EndOpeningAngle(), // end_opening
482  ClusterParamAlgo.Integral().value(), // integral
483  ClusterParamAlgo.IntegralStdDev().value(), // integral_stddev
484  ClusterParamAlgo.SummedADC().value(), // summedADC
485  ClusterParamAlgo.SummedADCStdDev().value(), // summedADC_stddev
486  ClusterParamAlgo.NHits(), // n_hits
487  ClusterParamAlgo.MultipleHitDensity(), // multiple_hit_density
488  cl1.Width(), // width
489  clusterID, // ID
490  cl1.View(), // view
491  cl1.Plane(), // planeID
492  recob::Cluster::Sentry // sentry
493  );
494 
495  util::CreateAssn(evt, *SuperClusters, cl1.Hits(), *assn);
496  ++clsnum1;
497 
498  } // end loop over first cluster iterator
499  } // end loop over planes
500 
501  mf::LogVerbatim("Summary") << std::setfill('-') << std::setw(175) << "-" << std::setfill(' ');
502  mf::LogVerbatim("Summary") << "LineMerger Summary:";
503  for (size_t i = 0; i < SuperClusters->size(); ++i)
504  mf::LogVerbatim("Summary") << SuperClusters->at(i);
505 
506  evt.put(std::move(SuperClusters));
507  evt.put(std::move(assn));
508  }
509 
510  //------------------------------------------------------------------------------------//
511  //checks the difference between angles of the two lines
512  bool LineMerger::SlopeCompatibility(double slope1, double slope2)
513  {
514  double sl1 = atan(slope1);
515  double sl2 = atan(slope2);
516 
517  //the units of fSlope are radians
518  return std::abs(sl1 - sl2) < fSlope;
519  }
520  //------------------------------------------------------------------------------------//
521  int LineMerger::EndpointCompatibility(float sclstartwire,
522  float sclstarttime,
523  float sclendwire,
524  float sclendtime,
525  float cl2startwire,
526  float cl2starttime,
527  float cl2endwire,
528  float cl2endtime)
529  {
530 
532  float distance =
533  std::sqrt((pow(sclendwire - cl2startwire, 2) * 13.5) + pow(sclendtime - cl2starttime, 2));
534 
535  //not sure if this line is necessary--spitz
536  float distance2 =
537  std::sqrt((pow(sclstartwire - cl2endwire, 2) * 13.5) + pow(sclstarttime - cl2endtime, 2));
538 
539  //determine which way the two clusters should be merged. TY
540  int comp = 0;
541  if (distance < fEndpointWindow)
542  comp = 1;
543  else if (distance2 < fEndpointWindow)
544  comp = -1;
545  return comp;
546  }
547 
549 
550 } // end namespace
float EndOpeningAngle() const
Returns the opening angle at the end of the cluster.
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
int EndpointCompatibility(float sclstartwire, float sclstarttime, float sclendwire, float sclendtime, float cl2startwire, float cl2starttime, float cl2endwire, float cl2endtime)
float EndWire() const
Returns the wire coordinate of the end of the cluster.
ClusterAndHitMerger(recob::Cluster const &cluster, HitVector_t const &cluster_hits)
float SigmaStartWire() const
Returns the uncertainty on wire coordinate of the start of the cluster.
Utilities related to art service access.
float StartAngle() const
Returns the starting angle of the cluster.
float SigmaWireCoord(ClusterEnds_t side) const
Returns the uncertainty on wire coordinate of one of the end sides of the cluster.
Definition: Cluster.h:414
float EdgeCharge(ClusterEnds_t side) const
Returns the charge on the first or last wire of the cluster.
float Angle(ClusterEnds_t side) const
Returns the angle at either end of the cluster.
Definition: Cluster.h:553
ClusterMerger(recob::Cluster const &cluster)
float SigmaTickCoord(ClusterEnds_t side) const
Returns the uncertainty on tick coordinate of one of the end sides of the cluster.
enum geo::_plane_proj View_t
Enumerate the possible plane projections.
void AddHits(TCSlice &slc, Trajectory &tj, unsigned short ipt, bool &sigOK)
Definition: StepUtils.cxx:1113
Planes which measure V.
Definition: geo_types.h:132
float TickCoord(ClusterEnds_t side) const
Returns the tick coordinate of one of the end sides of the cluster.
Definition: Cluster.h:401
Declaration of signal hit object.
void produce(art::Event &evt) override
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.cc:6
The data type to uniquely identify a Plane.
Definition: geo_types.h:364
float EdgeCharge(ClusterEnds_t side) const
Returns the charge on the first or last wire of the cluster.
Definition: Cluster.h:528
float SigmaTickCoord(ClusterEnds_t side) const
Returns the uncertainty on tick coordinate of one of the end sides of the cluster.
Definition: Cluster.h:427
constexpr auto abs(T v)
Returns the absolute value of the argument.
float StartWire() const
Returns the wire coordinate of the start of the cluster.
Definition: Cluster.h:276
Planes which measure Z direction.
Definition: geo_types.h:134
Set of hits with a 2D structure.
Definition: Cluster.h:69
geo::PlaneID Plane() const
Returns the plane ID this cluster lies on.
Definition: Cluster.h:717
Cluster finding and building.
static void bot(T &var, T value)
Data referring to start and end of the cluster.
float fWidth
A measure of the cluster width, in homogenized units.
bool isValid() const
Returns if the cluster is valid (that is, if its ID is not invalid)
Definition: Cluster.h:725
float SigmaStartTick() const
Data referring to start and end of the cluster.
float StartWire() const
Returns the wire coordinate of the start of the cluster.
static const SentryArgument_t Sentry
An instance of the sentry object.
Definition: Cluster.h:174
float StartOpeningAngle() const
Returns the opening angle at the start of the cluster.
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Event.h:77
LineMerger(fhicl::ParameterSet const &pset)
bool Add(recob::Cluster const &cluster, HitVector_t const &cluster_hits, bool prepend=false)
Merges a single cluster into this object.
Planes which measure U.
Definition: geo_types.h:131
float TickCoord(ClusterEnds_t side) const
Returns the tick coordinate of one of the end sides of the cluster.
void hits()
Definition: readHits.C:15
TCanvas * c2
Definition: plot_hist.C:75
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:65
static void top(T &var, T value)
Data referring to start and end of the cluster.
std::string fClusterModuleLabel
Wrapper for ClusterParamsAlgBase objects to accept diverse input.
void AdoptEnd(recob::Cluster const &cluster, ClusterEnds_t iEnd)
Imports all the member of the corresponding end.
float SigmaEndWire() const
Returns the uncertainty on wire coordinate of the end of the cluster.
float Width() const
A measure of the cluster width, in homogenized units.
Definition: Cluster.h:701
float SigmaEndTick() const
Returns the uncertainty on tick coordinate of the end of the cluster.
Wrapper for ClusterParamsAlgBase objects to accept arbitrary input.
float Angle(ClusterEnds_t side) const
Returns the angle at either end of the cluster.
Declaration of cluster object.
float WireCoord(ClusterEnds_t side) const
Returns the wire coordinate of one of the end sides of the cluster.
Definition: Cluster.h:374
Definition of data types for geometry description.
bool hasPlane() const
Returns whether geometry plane is valid.
float SigmaWireCoord(ClusterEnds_t side) const
Returns the uncertainty on wire coordinate of one of the end sides of the cluster.
double value
Definition: spectrum.C:18
float StartTick() const
Returns the tick coordinate of the start of the cluster.
Class merging clusters: recomputes start and end position and hit list.
float Width() const
A measure of the cluster width, in homogenized units.
HitVector_t hits
hits in the cluster
bool CreateAssn(art::Event &evt, std::vector< T > const &a, art::Ptr< U > const &b, art::Assns< U, T > &assn, std::string a_instance, size_t index=UINT_MAX)
Creates a single one-to-one association.
HitVector_t const & Hits() const
Returns a constant reference to the current list of hits.
geo::View_t View() const
Returns the view for this cluster.
Definition: Cluster.h:714
geo::View_t fView
View for this cluster.
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
float StartCharge() const
Returns the charge on the first wire of the cluster.
Utility object to perform functions of association.
float OpeningAngle(ClusterEnds_t side) const
Returns the opening angle at either end of the cluster.
ID_t ID() const
Identifier of this cluster.
Definition: Cluster.h:711
bool SlopeCompatibility(double slope1, double slope2)
unsigned int NHits() const
Number of hits in the cluster.
bool hasPlane() const
Returns whether geometry plane is valid.
Definition: Cluster.h:722
geo::View_t View() const
Returns the view for this cluster.
Interface to class computing cluster parameters.
TCEvent evt
Definition: DataStructs.cxx:8
geo::PlaneID Plane() const
Returns the plane ID this cluster lies on.
std::vector< HitPtr_t > HitVector_t
vector of pointers to hits
recob::tracking::Plane Plane
Definition: TrackState.h:17
Class merging clusters: recomputes start and end position and hit list.
float OpeningAngle(ClusterEnds_t side) const
Returns the opening angle at either end of the cluster.
Definition: Cluster.h:568
float EndAngle() const
Returns the ending angle of the cluster.
recob::Cluster::ID_t ID_t
Type of cluster ID.
float WireCoord(ClusterEnds_t side) const
Returns the wire coordinate of one of the end sides of the cluster.
art framework interface to geometry description
int ID_t
Type of cluster ID.
Definition: Cluster.h:72
void AddHits(HitVector_t const &cluster_hits, bool prepend)
geo::PlaneID fPlaneID
Location of the start of the cluster.
float EndCharge() const
Returns the charge on the last wire of the cluster.
float EndTick() const
Returns the tick coordinate of the end of the cluster.
bool Add(recob::Cluster const &cluster)
Merges a single cluster into this object.
float EndWire() const
Returns the wire coordinate of the end of the cluster.
Definition: Cluster.h:318