LArSoft  v10_04_05
Liquid Argon Software toolkit - https://larsoft.org/
StandardHit3DBuilder_tool.cc
Go to the documentation of this file.
1 
8 // Framework Includes
15 #include "art_root_io/TFileDirectory.h"
16 #include "art_root_io/TFileService.h"
21 #include "cetlib/cpu_timer.h"
22 #include "fhiclcpp/ParameterSet.h"
24 
25 // LArSoft includes
31 #include "larevt/CalibrationDBI/Interface/ChannelStatusProvider.h"
32 #include "larevt/CalibrationDBI/Interface/ChannelStatusService.h"
34 
35 // Eigen
36 #include <Eigen/Core>
37 
38 // std includes
39 #include <iostream>
40 #include <memory>
41 #include <numeric> // std::accumulate
42 #include <string>
43 
44 // Ack!
45 #include "TH1F.h"
46 #include "TTree.h"
47 
48 //------------------------------------------------------------------------------------------------------------------------------------------
49 // implementation follows
50 
51 namespace lar_cluster3d {
52 
58  // forward declaration to define an ordering function for our hit set
59  struct Hit2DSetCompare {
60  bool operator()(const reco::ClusterHit2D*, const reco::ClusterHit2D*) const;
61  };
62 
63  using HitVector = std::vector<const reco::ClusterHit2D*>;
64  using PlaneToHitVectorMap = std::map<geo::PlaneID, HitVector>;
65  using TPCToPlaneToHitVectorMap = std::map<geo::TPCID, PlaneToHitVectorMap>;
66  using Hit2DList = std::list<reco::ClusterHit2D>;
67  using Hit2DSet = std::set<const reco::ClusterHit2D*, Hit2DSetCompare>;
68  using WireToHitSetMap = std::map<unsigned int, Hit2DSet>;
69  using PlaneToWireToHitSetMap = std::map<geo::PlaneID, WireToHitSetMap>;
70  using TPCToPlaneToWireToHitSetMap = std::map<geo::TPCID, PlaneToWireToHitSetMap>;
71  using HitVectorMap = std::map<size_t, HitVector>;
72 
77  public:
83  explicit StandardHit3DBuilder(fhicl::ParameterSet const& pset);
84 
89  void produces(art::ProducesCollector&) override;
90 
97  void Hit3DBuilder(art::Event&, reco::HitPairList&, RecobHitToPtrMap&) override;
98 
102  float getTimeToExecute(IHit3DBuilder::TimeValues index) const override
103  {
104  return m_timeVector[index];
105  }
106 
107  private:
113  void CollectArtHits(const art::Event& evt) const;
114 
118  void BuildHit3D(reco::HitPairList& hitPairList) const;
119 
123  void CreateNewRecobHitCollection(art::Event&,
125  std::vector<recob::Hit>&,
127 
131  void makeWireAssns(const art::Event&,
133  RecobHitToPtrMap&) const;
134 
138  void makeRawDigitAssns(const art::Event&,
140  RecobHitToPtrMap&) const;
141 
145  size_t BuildHitPairMap(PlaneToHitVectorMap& planeToHitVectorMap,
146  reco::HitPairList& hitPairList) const;
147 
152  std::vector<std::pair<HitVector::iterator, HitVector::iterator>>;
153 
154  size_t BuildHitPairMapByTPC(PlaneHitVectorItrPairVec& planeHitVectorItrPairVec,
155  reco::HitPairList& hitPairList) const;
156 
160  using HitMatchPair = std::pair<const reco::ClusterHit2D*, reco::ClusterHit3D>;
161  using HitMatchPairVec = std::vector<HitMatchPair>;
162  using HitMatchPairVecMap = std::map<geo::WireID, HitMatchPairVec>;
163 
164  int findGoodHitPairs(const reco::ClusterHit2D*,
167  HitMatchPairVecMap&) const;
168 
172  void findGoodTriplets(HitMatchPairVecMap&, HitMatchPairVecMap&, reco::HitPairList&) const;
173 
177  bool makeHitPair(reco::ClusterHit3D& pairOut,
178  const reco::ClusterHit2D* hit1,
179  const reco::ClusterHit2D* hit2,
180  float hitWidthSclFctr = 1.,
181  size_t hitPairCntr = 0) const;
182 
186  bool makeHitTriplet(reco::ClusterHit3D& pairOut,
187  const reco::ClusterHit3D& pairIn,
188  const reco::ClusterHit2D* hit2) const;
189 
193  bool makeDeadChannelPair(reco::ClusterHit3D& pairOut,
194  const reco::ClusterHit3D& pair,
195  size_t maxStatus,
196  size_t minStatus) const;
197 
201  const reco::ClusterHit2D* FindBestMatchingHit(const Hit2DSet& hit2DSet,
202  const reco::ClusterHit3D& pair,
203  float pairDeltaTimeLimits) const;
204 
208  int FindNumberInRange(const Hit2DSet& hit2DSet,
209  const reco::ClusterHit3D& pair,
210  float range) const;
211 
215  geo::WireID NearestWireID(const Eigen::Vector3f& position, const geo::WireID& wireID) const;
216 
220  float DistanceFromPointToHitWire(const Eigen::Vector3f& position,
221  const geo::WireID& wireID) const;
222 
226  void BuildChannelStatusVec() const;
227 
231  float chargeIntegral(float, float, float, int, int) const;
232 
236  using ChannelStatusVec = std::vector<size_t>;
237  using ChannelStatusByPlaneVec = std::vector<ChannelStatusVec>;
238 
242  void clear();
243 
247  std::vector<art::InputTag> m_hitFinderTagVec;
251  std::vector<int> m_invalidTPCVec;
252  float
256 
258  float m_wirePitch[3];
259  mutable std::vector<float> m_timeVector;
260 
262 
263  // Define some basic histograms
264  TTree* m_tupleTree;
265 
266  mutable std::vector<float> m_deltaTimeVec;
267  mutable std::vector<float> m_chiSquare3DVec;
268  mutable std::vector<float> m_maxPullVec;
269  mutable std::vector<float> m_overlapFractionVec;
270  mutable std::vector<float> m_overlapRangeVec;
271  mutable std::vector<float> m_maxDeltaPeakVec;
272  mutable std::vector<float> m_maxSideVecVec;
273  mutable std::vector<float> m_pairWireDistVec;
274  mutable std::vector<float> m_smallChargeDiffVec;
275  mutable std::vector<int> m_smallIndexVec;
276  mutable std::vector<float> m_qualityMetricVec;
277  mutable std::vector<float> m_spacePointChargeVec;
278  mutable std::vector<float> m_hitAsymmetryVec;
279 
280  // Get instances of the primary data structures needed
284 
286  mutable size_t m_numBadChannels;
287 
288  mutable bool m_weHaveAllBeenHereBefore = false;
289 
292  const lariov::ChannelStatusProvider* m_channelFilter;
293  };
294 
296  : m_geometry(art::ServiceHandle<geo::Geometry const>{}.get())
298  {
299  m_hitFinderTagVec = pset.get<std::vector<art::InputTag>>("HitFinderTagVec",
300  std::vector<art::InputTag>{"gaushit"});
301  m_enableMonitoring = pset.get<bool>("EnableMonitoring", true);
302  m_numSigmaPeakTime = pset.get<float>("NumSigmaPeakTime", 3.);
303  m_hitWidthSclFctr = pset.get<float>("HitWidthScaleFactor", 6.);
304  m_deltaPeakTimeSig = pset.get<float>("DeltaPeakTimeSig", 1.7);
305  m_zPosOffset = pset.get<float>("ZPosOffset", 0.0);
306  m_invalidTPCVec = pset.get<std::vector<int>>("InvalidTPCVec", std::vector<int>{});
307  m_wirePitchScaleFactor = pset.get<float>("WirePitchScaleFactor", 1.9);
308  m_maxHit3DChiSquare = pset.get<float>("MaxHitChiSquare", 6.0);
309  m_outputHistograms = pset.get<bool>("OutputHistograms", false);
310 
313 
314  // Returns the wire pitch per plane assuming they will be the same for all TPCs
315  constexpr geo::TPCID tpcid{0, 0};
316  m_wirePitch[0] = m_wireReadoutGeom->Plane({tpcid, 0}).WirePitch();
317  m_wirePitch[1] = m_wireReadoutGeom->Plane({tpcid, 1}).WirePitch();
318  m_wirePitch[2] = m_wireReadoutGeom->Plane({tpcid, 2}).WirePitch();
319 
320  if (m_outputHistograms) {
321  // Access ART's TFileService, which will handle creating and writing
322  // histograms and n-tuples for us.
324 
325  m_tupleTree = tfs->make<TTree>("Hit3DBuilderTree", "Tree by StandardHit3DBuilder");
326 
327  clear();
328 
329  m_tupleTree->Branch("DeltaTime2D", "std::vector<float>", &m_deltaTimeVec);
330  m_tupleTree->Branch("ChiSquare3D", "std::vector<float>", &m_chiSquare3DVec);
331  m_tupleTree->Branch("MaxPullValue", "std::vector<float>", &m_maxPullVec);
332  m_tupleTree->Branch("OverlapFraction", "std::vector<float>", &m_overlapFractionVec);
333  m_tupleTree->Branch("OverlapRange", "std::vector<float>", &m_overlapRangeVec);
334  m_tupleTree->Branch("MaxDeltaPeak", "std::vector<float>", &m_maxDeltaPeakVec);
335  m_tupleTree->Branch("MaxSideVec", "std::vector<float>", &m_maxSideVecVec);
336  m_tupleTree->Branch("PairWireDistVec", "std::vector<float>", &m_pairWireDistVec);
337  m_tupleTree->Branch("SmallChargeDiff", "std::vector<float>", &m_smallChargeDiffVec);
338  m_tupleTree->Branch("SmallChargeIdx", "std::vector<int>", &m_smallIndexVec);
339  m_tupleTree->Branch("QualityMetric", "std::vector<float>", &m_qualityMetricVec);
340  m_tupleTree->Branch("SPCharge", "std::vector<float>", &m_spacePointChargeVec);
341  m_tupleTree->Branch("HitAsymmetry", "std::vector<float>", &m_hitAsymmetryVec);
342  }
343  }
344 
345  //------------------------------------------------------------------------------------------------------------------------------------------
346 
348  {
349  collector.produces<std::vector<recob::Hit>>();
352  }
353 
354  //------------------------------------------------------------------------------------------------------------------------------------------
355 
357  {
358  m_deltaTimeVec.clear();
359  m_chiSquare3DVec.clear();
360  m_maxPullVec.clear();
361  m_overlapFractionVec.clear();
362  m_overlapRangeVec.clear();
363  m_maxDeltaPeakVec.clear();
364  m_maxSideVecVec.clear();
365  m_pairWireDistVec.clear();
366  m_smallChargeDiffVec.clear();
367  m_smallIndexVec.clear();
368  m_qualityMetricVec.clear();
369  m_spacePointChargeVec.clear();
370  m_hitAsymmetryVec.clear();
371  }
372 
374  {
375  // This is called each event, clear out the previous version and start over
376  if (!m_channelStatus.empty()) m_channelStatus.clear();
377 
378  m_numBadChannels = 0;
380 
381  // Loop through views/planes to set the wire length vectors
382  constexpr geo::TPCID tpcid{0, 0};
383  for (unsigned int idx = 0; idx < m_channelStatus.size(); idx++) {
384  m_channelStatus[idx] =
386  }
387 
388  // Loop through the channels and mark those that are "bad"
389  for (size_t channel = 0; channel < m_wireReadoutGeom->Nchannels(); channel++) {
390  if (m_channelFilter->IsGood(channel)) continue;
391 
392  std::vector<geo::WireID> wireIDVec = m_wireReadoutGeom->ChannelToWire(channel);
393  geo::WireID wireID = wireIDVec[0];
394  lariov::ChannelStatusProvider::Status_t chanStat = m_channelFilter->Status(channel);
395 
396  m_channelStatus[wireID.Plane][wireID.Wire] = chanStat;
398  }
399  }
400 
403  {
404  return (*left).getAvePeakTime() < (*right).getAvePeakTime();
405  }
406 
407  struct HitPairClusterOrder {
410  {
411  // Watch out for the case where two clusters can have the same number of hits!
412  if (left->second.size() == right->second.size()) return left->first < right->first;
413 
414  return left->second.size() > right->second.size();
415  }
416  };
417 
419  reco::HitPairList& hitPairList,
420  RecobHitToPtrMap& clusterHitToArtPtrMap)
421  {
422  // Clear the internal data structures
423  m_clusterHit2DMasterList.clear();
424  m_planeToHitVectorMap.clear();
425  m_planeToWireToHitSetMap.clear();
426 
427  m_timeVector.resize(NUMTIMEVALUES, 0.);
428 
429  // Get a hit refiner
430  std::unique_ptr<std::vector<recob::Hit>> outputHitPtrVec(new std::vector<recob::Hit>);
431 
432  // Recover the 2D hits and then organize them into data structures which will be used in the
433  // DBscan algorithm for building the 3D clusters
434  CollectArtHits(evt);
435 
436  // If there are no hits in our view/wire data structure then do not proceed with the full analysis
437  if (!m_planeToWireToHitSetMap.empty()) {
438  // Call the algorithm that builds 3D hits
439  BuildHit3D(hitPairList);
440 
441  // If we built 3D points then attempt to output a new hit list as well
442  if (!hitPairList.empty())
443  CreateNewRecobHitCollection(evt, hitPairList, *outputHitPtrVec, clusterHitToArtPtrMap);
444  }
445 
446  // Set up to make the associations (if desired)
448  std::unique_ptr<art::Assns<recob::Wire, recob::Hit>> wireAssns(
450 
451  makeWireAssns(evt, *wireAssns, clusterHitToArtPtrMap);
452 
454  std::unique_ptr<art::Assns<raw::RawDigit, recob::Hit>> rawDigitAssns(
456 
457  makeRawDigitAssns(evt, *rawDigitAssns, clusterHitToArtPtrMap);
458 
459  // Move everything into the event
460  evt.put(std::move(outputHitPtrVec));
461  evt.put(std::move(wireAssns));
462  evt.put(std::move(rawDigitAssns));
463 
464  // Handle tree output too
465  if (m_outputHistograms) {
466  m_tupleTree->Fill();
467 
468  clear();
469  }
470  }
471 
473  {
478  cet::cpu_timer theClockMakeHits;
479 
480  if (m_enableMonitoring) theClockMakeHits.start();
481 
482  // The first task is to take the lists of input 2D hits (a map of view to sorted lists of 2D hits)
483  // and then to build a list of 3D hits to be used in downstream processing
485 
486  size_t numHitPairs = BuildHitPairMap(m_planeToHitVectorMap, hitPairList);
487 
488  if (m_enableMonitoring) {
489  theClockMakeHits.stop();
490 
491  m_timeVector[BUILDTHREEDHITS] = theClockMakeHits.accumulated_real_time();
492  }
493 
494  mf::LogDebug("Cluster3D") << ">>>>> 3D hit building done, found " << numHitPairs << " 3D Hits"
495  << std::endl;
496  }
497 
498  //------------------------------------------------------------------------------------------------------------------------------------------
499  namespace {
500 
501  class SetHitEarliestTimeOrder {
502  public:
503  SetHitEarliestTimeOrder() : m_numRMS(1.) {}
504  SetHitEarliestTimeOrder(float numRMS) : m_numRMS(numRMS) {}
505 
506  bool operator()(const reco::ClusterHit2D* left, const reco::ClusterHit2D* right) const
507  {
508  return left->getTimeTicks() - m_numRMS * left->getHit()->RMS() <
509  right->getTimeTicks() - m_numRMS * right->getHit()->RMS();
510  }
511 
512  public:
513  float m_numRMS;
514  };
515 
516  using HitVectorItrPair = std::pair<HitVector::iterator, HitVector::iterator>;
517 
518  class SetStartTimeOrder {
519  public:
520  SetStartTimeOrder() : m_numRMS(1.) {}
521  SetStartTimeOrder(float numRMS) : m_numRMS(numRMS) {}
522 
523  bool operator()(const HitVectorItrPair& left, const HitVectorItrPair& right) const
524  {
525  // Protect against possible issue?
526  if (left.first != left.second && right.first != right.second) {
527  // Sort by "modified start time" of pulse
528  return (*left.first)->getTimeTicks() - m_numRMS * (*left.first)->getHit()->RMS() <
529  (*right.first)->getTimeTicks() - m_numRMS * (*right.first)->getHit()->RMS();
530  }
531 
532  return left.first != left.second;
533  }
534 
535  private:
536  float m_numRMS;
537  };
538 
539  bool SetPairStartTimeOrder(const reco::ClusterHit3D& left, const reco::ClusterHit3D& right)
540  {
541  // Sort by "modified start time" of pulse
542  return left.getAvePeakTime() - left.getSigmaPeakTime() <
543  right.getAvePeakTime() - right.getSigmaPeakTime();
544  }
545  }
546 
547  //------------------------------------------------------------------------------------------------------------------------------------------
548 
550  reco::HitPairList& hitPairList) const
551  {
561  size_t totalNumHits(0);
562  size_t hitPairCntr(0);
563 
564  size_t nTriplets(0);
565  size_t nDeadChanHits(0);
566 
567  // Set up to loop over cryostats and tpcs...
568  for (size_t cryoIdx = 0; cryoIdx < m_geometry->Ncryostats(); cryoIdx++) {
569  for (size_t tpcIdx = 0; tpcIdx < m_geometry->NTPC(); tpcIdx++) {
571  planeToHitVectorMap.find(geo::PlaneID(cryoIdx, tpcIdx, 0));
573  planeToHitVectorMap.find(geo::PlaneID(cryoIdx, tpcIdx, 1));
575  planeToHitVectorMap.find(geo::PlaneID(cryoIdx, tpcIdx, 2));
576 
577  size_t nPlanesWithHits =
578  (mapItr0 != planeToHitVectorMap.end() && !mapItr0->second.empty() ? 1 : 0) +
579  (mapItr1 != planeToHitVectorMap.end() && !mapItr1->second.empty() ? 1 : 0) +
580  (mapItr2 != planeToHitVectorMap.end() && !mapItr2->second.empty() ? 1 : 0);
581 
582  if (nPlanesWithHits < 2) continue;
583 
584  HitVector& hitVector0 = mapItr0->second;
585  HitVector& hitVector1 = mapItr1->second;
586  HitVector& hitVector2 = mapItr2->second;
587 
588  // We are going to resort the hits into "start time" order...
589  std::sort(hitVector0.begin(),
590  hitVector0.end(),
591  SetHitEarliestTimeOrder(m_numSigmaPeakTime)); //SetHitStartTimeOrder);
592  std::sort(hitVector1.begin(),
593  hitVector1.end(),
594  SetHitEarliestTimeOrder(m_numSigmaPeakTime)); //SetHitStartTimeOrder);
595  std::sort(hitVector2.begin(),
596  hitVector2.end(),
597  SetHitEarliestTimeOrder(m_numSigmaPeakTime)); //SetHitStartTimeOrder);
598 
599  PlaneHitVectorItrPairVec hitItrVec = {
600  HitVectorItrPair(hitVector0.begin(), hitVector0.end()),
601  HitVectorItrPair(hitVector1.begin(), hitVector1.end()),
602  HitVectorItrPair(hitVector2.begin(), hitVector2.end())};
603 
604  totalNumHits += BuildHitPairMapByTPC(hitItrVec, hitPairList);
605  }
606  }
607 
608  // Return the hit pair list but sorted by z and y positions (faster traversal in next steps)
609  hitPairList.sort(SetPairStartTimeOrder);
610 
611  // Where are we?
612  mf::LogDebug("Cluster3D") << "Total number hits: " << totalNumHits << std::endl;
613  mf::LogDebug("Cluster3D") << "Created a total of " << hitPairList.size()
614  << " hit pairs, counted: " << hitPairCntr << std::endl;
615  mf::LogDebug("Cluster3D") << "-- Triplets: " << nTriplets
616  << ", dead channel pairs: " << nDeadChanHits << std::endl;
617 
618  return hitPairList.size();
619  }
620 
622  reco::HitPairList& hitPairList) const
623  {
634  // Define functions to set start/end iterators in the loop below
635  auto SetStartIterator =
636  [](HitVector::iterator startItr, HitVector::iterator endItr, float rms, float startTime) {
637  while (startItr != endItr) {
638  float numRMS(rms);
639  if ((*startItr)->getTimeTicks() + numRMS * (*startItr)->getHit()->RMS() < startTime)
640  startItr++;
641  else
642  break;
643  }
644  return startItr;
645  };
646 
647  auto SetEndIterator =
648  [](HitVector::iterator firstItr, HitVector::iterator endItr, float rms, float endTime) {
649  while (firstItr != endItr) {
650  float numRMS(rms);
651  if ((*firstItr)->getTimeTicks() - numRMS * (*firstItr)->getHit()->RMS() < endTime)
652  firstItr++;
653  else
654  break;
655  }
656  return firstItr;
657  };
658 
659  //*********************************************************************************
660  // Basically, we try to loop until done...
661  while (1) {
662  // Sort so that the earliest hit time will be the first element, etc.
663  std::sort(hitItrVec.begin(), hitItrVec.end(), SetStartTimeOrder(m_numSigmaPeakTime));
664 
665  // This loop iteration's golden hit
666  const reco::ClusterHit2D* goldenHit = *hitItrVec[0].first;
667 
668  // The range of history... (for this hit)
669  float goldenTimeStart = goldenHit->getTimeTicks() -
670  m_numSigmaPeakTime * goldenHit->getHit()->RMS() -
671  std::numeric_limits<float>::epsilon();
672  float goldenTimeEnd = goldenHit->getTimeTicks() +
673  m_numSigmaPeakTime * goldenHit->getHit()->RMS() +
674  std::numeric_limits<float>::epsilon();
675 
676  // Set iterators to insure we'll be in the overlap ranges
677  HitVector::iterator hitItr1Start = SetStartIterator(
678  hitItrVec[1].first, hitItrVec[1].second, m_numSigmaPeakTime, goldenTimeStart);
679  HitVector::iterator hitItr1End =
680  SetEndIterator(hitItr1Start, hitItrVec[1].second, m_numSigmaPeakTime, goldenTimeEnd);
681  HitVector::iterator hitItr2Start = SetStartIterator(
682  hitItrVec[2].first, hitItrVec[2].second, m_numSigmaPeakTime, goldenTimeStart);
683  HitVector::iterator hitItr2End =
684  SetEndIterator(hitItr2Start, hitItrVec[2].second, m_numSigmaPeakTime, goldenTimeEnd);
685 
686  // Since we'll use these many times in the internal loops, pre make the pairs for the second set of hits
687  HitMatchPairVecMap pair12Map;
688  HitMatchPairVecMap pair13Map;
689 
690  size_t n12Pairs = findGoodHitPairs(goldenHit, hitItr1Start, hitItr1End, pair12Map);
691  size_t n13Pairs = findGoodHitPairs(goldenHit, hitItr2Start, hitItr2End, pair13Map);
692 
693  if (n12Pairs > n13Pairs)
694  findGoodTriplets(pair12Map, pair13Map, hitPairList);
695  else
696  findGoodTriplets(pair13Map, pair12Map, hitPairList);
697 
698  hitItrVec[0].first++;
699 
700  int nPlanesWithHits(0);
701 
702  for (auto& pair : hitItrVec)
703  if (pair.first != pair.second) nPlanesWithHits++;
704 
705  if (nPlanesWithHits < 2) break;
706  }
707 
708  return hitPairList.size();
709  }
710 
712  HitVector::iterator& startItr,
713  HitVector::iterator& endItr,
714  HitMatchPairVecMap& hitMatchMap) const
715  {
716  int numPairs(0);
717 
718  // Loop through the input secon hits and make pairs
719  while (startItr != endItr) {
720  const reco::ClusterHit2D* hit = *startItr++;
721  reco::ClusterHit3D pair;
722 
723  // pair returned with a negative ave time is signal of failure
724  if (!makeHitPair(pair, goldenHit, hit, m_hitWidthSclFctr)) continue;
725 
726  geo::WireID wireID = hit->WireID();
727 
728  hitMatchMap[wireID].emplace_back(hit, pair);
729 
730  numPairs++;
731  }
732 
733  return numPairs;
734  }
735 
737  HitMatchPairVecMap& pair13Map,
738  reco::HitPairList& hitPairList) const
739  {
740  // Build triplets from the two lists of hit pairs
741  if (!pair12Map.empty()) {
742  // temporary container for dead channel hits
743  std::vector<reco::ClusterHit3D> tempDeadChanVec;
744  reco::ClusterHit3D deadChanPair;
745 
746  // Keep track of which third plane hits have been used
747  std::map<const reco::ClusterHit3D*, bool> usedPairMap;
748 
749  // Initial population of this map with the pair13Map hits
750  for (const auto& pair13 : pair13Map) {
751  for (const auto& hit2Dhit3DPair : pair13.second)
752  usedPairMap[&hit2Dhit3DPair.second] = false;
753  }
754 
755  // The outer loop is over all hit pairs made from the first two plane combinations
756  for (const auto& pair12 : pair12Map) {
757  if (pair12.second.empty()) continue;
758 
759  // This loop is over hit pairs that share the same first two plane wires but may have different
760  // hit times on those wires
761  for (const auto& hit2Dhit3DPair12 : pair12.second) {
762  const reco::ClusterHit3D& pair1 = hit2Dhit3DPair12.second;
763 
764  // populate the map with initial value
765  usedPairMap[&pair1] = false;
766 
767  // The simplest approach here is to loop over all possibilities and let the triplet builder weed out the weak candidates
768  for (const auto& pair13 : pair13Map) {
769  if (pair13.second.empty()) continue;
770 
771  for (const auto& hit2Dhit3DPair13 : pair13.second) {
772  const reco::ClusterHit2D* hit2 = hit2Dhit3DPair13.first;
773  const reco::ClusterHit3D& pair2 = hit2Dhit3DPair13.second;
774 
775  // If success try for the triplet
776  reco::ClusterHit3D triplet;
777 
778  if (makeHitTriplet(triplet, pair1, hit2)) {
779  triplet.setID(hitPairList.size());
780  hitPairList.emplace_back(triplet);
781  usedPairMap[&pair1] = true;
782  usedPairMap[&pair2] = true;
783  }
784  }
785  }
786  }
787  }
788 
789  // One more loop through the other pairs to check for sick channels
790  if (m_numBadChannels > 0) {
791  for (const auto& pairMapPair : usedPairMap) {
792  if (pairMapPair.second) continue;
793 
794  const reco::ClusterHit3D* pair = pairMapPair.first;
795 
796  // Here we look to see if we failed to make a triplet because the partner wire was dead/noisy/sick
797  if (makeDeadChannelPair(deadChanPair, *pair, 4, 0))
798  tempDeadChanVec.emplace_back(deadChanPair);
799  }
800 
801  // Handle the dead wire triplets
802  if (!tempDeadChanVec.empty()) {
803  // If we have many then see if we can trim down a bit by keeping those with time significance
804  if (tempDeadChanVec.size() > 1) {
805  // Sort by "significance" of agreement
806  std::sort(tempDeadChanVec.begin(),
807  tempDeadChanVec.end(),
808  [](const auto& left, const auto& right) {
809  return left.getDeltaPeakTime() / left.getSigmaPeakTime() <
810  right.getDeltaPeakTime() / right.getSigmaPeakTime();
811  });
812 
813  // What is the range of "significance" from first to last?
814  float firstSig = tempDeadChanVec.front().getDeltaPeakTime() /
815  tempDeadChanVec.front().getSigmaPeakTime();
816  float lastSig =
817  tempDeadChanVec.back().getDeltaPeakTime() / tempDeadChanVec.back().getSigmaPeakTime();
818  float sigRange = lastSig - firstSig;
819 
820  if (lastSig > 0.5 * m_deltaPeakTimeSig && sigRange > 0.5) {
821  // Declare a maximum of 1.5 * the average of the first and last pairs...
822  float maxSignificance = std::max(0.75 * (firstSig + lastSig), 1.0);
823 
824  std::vector<reco::ClusterHit3D>::iterator firstBadElem = std::find_if(
825  tempDeadChanVec.begin(),
826  tempDeadChanVec.end(),
827  [&maxSignificance](const auto& pair) {
828  return pair.getDeltaPeakTime() / pair.getSigmaPeakTime() > maxSignificance;
829  });
830 
831  // But only keep the best 10?
832  if (std::distance(tempDeadChanVec.begin(), firstBadElem) > 20)
833  firstBadElem = tempDeadChanVec.begin() + 20;
834  // Keep at least one hit...
835  else if (firstBadElem == tempDeadChanVec.begin())
836  firstBadElem++;
837 
838  tempDeadChanVec.resize(std::distance(tempDeadChanVec.begin(), firstBadElem));
839  }
840  }
841 
842  for (auto& pair : tempDeadChanVec) {
843  pair.setID(hitPairList.size());
844  hitPairList.emplace_back(pair);
845  }
846  }
847  }
848  }
849 
850  return;
851  }
852 
854  const reco::ClusterHit2D* hit1,
855  const reco::ClusterHit2D* hit2,
856  float hitWidthSclFctr,
857  size_t hitPairCntr) const
858  {
859  // Assume failure
860  bool result(false);
861 
862  // We assume in this routine that we are looking at hits in different views
863  // The first mission is to check that the wires intersect
864  const geo::WireID& hit1WireID = hit1->WireID();
865  const geo::WireID& hit2WireID = hit2->WireID();
866 
867  if (auto widIntersect = m_wireReadoutGeom->WireIDsIntersect(hit1WireID, hit2WireID)) {
868  // Wires intersect so now we can check the timing
869  float hit1Peak = hit1->getTimeTicks();
870  float hit1Sigma = hit1->getHit()->RMS();
871 
872  float hit2Peak = hit2->getTimeTicks();
873  float hit2Sigma = hit2->getHit()->RMS();
874 
875  // "Long hits" are an issue... so we deal with these differently
876  int hit1NDF = hit1->getHit()->DegreesOfFreedom();
877  int hit2NDF = hit2->getHit()->DegreesOfFreedom();
878 
879  // Basically, allow the range to extend to the nearest end of the snippet
880  if (hit1NDF < 2)
881  hit1Sigma = std::min(hit1Peak - float(hit1->getHit()->StartTick()),
882  float(hit1->getHit()->EndTick()) - hit1Peak);
883  if (hit2NDF < 2)
884  hit2Sigma = std::min(hit2Peak - float(hit2->getHit()->StartTick()),
885  float(hit2->getHit()->EndTick()) - hit2Peak);
886 
887  // The "hit sigma" is the gaussian fit sigma of the hit, we need to expand a bit to allow hit overlap efficiency
888  float hit1Width = hitWidthSclFctr * hit1Sigma;
889  float hit2Width = hitWidthSclFctr * hit2Sigma;
890 
891  // Coarse check hit times are "in range"
892  if (fabs(hit1Peak - hit2Peak) <= (hit1Width + hit2Width)) {
893  // Check to see that hit peak times are consistent with each other
894  float hit1SigSq = hit1Sigma * hit1Sigma;
895  float hit2SigSq = hit2Sigma * hit2Sigma;
896  float deltaPeakTime = std::fabs(hit1Peak - hit2Peak);
897  float sigmaPeakTime = std::sqrt(hit1SigSq + hit2SigSq);
898 
899  // delta peak time consistency check here
900  if (deltaPeakTime < m_deltaPeakTimeSig *
901  sigmaPeakTime) // 2 sigma consistency? (do this way to avoid divide)
902  {
903  float oneOverWghts = hit1SigSq * hit2SigSq / (hit1SigSq + hit2SigSq);
904  float avePeakTime = (hit1Peak / hit1SigSq + hit2Peak / hit2SigSq) * oneOverWghts;
905  float totalCharge = hit1->getHit()->Integral() + hit2->getHit()->Integral();
906  float hitChiSquare = std::pow((hit1Peak - avePeakTime), 2) / hit1SigSq +
907  std::pow((hit2Peak - avePeakTime), 2) / hit2SigSq;
908 
909  float xPositionHit1(hit1->getXPosition());
910  float xPositionHit2(hit2->getXPosition());
911  float xPosition = (xPositionHit1 / hit1SigSq + xPositionHit2 / hit2SigSq) * hit1SigSq *
912  hit2SigSq / (hit1SigSq + hit2SigSq);
913 
914  Eigen::Vector3f position(
915  xPosition, float(widIntersect->y), float(widIntersect->z) - m_zPosOffset);
916 
917  // If to here then we need to sort out the hit pair code telling what views are used
918  unsigned statusBits = 1 << hit1->WireID().Plane | 1 << hit2->WireID().Plane;
919 
920  // handle status bits for the 2D hits
925 
928 
929  reco::ClusterHit2DVec hitVector;
930 
931  hitVector.resize(3, NULL);
932 
933  hitVector[hit1->WireID().Plane] = hit1;
934  hitVector[hit2->WireID().Plane] = hit2;
935 
936  unsigned int cryostatIdx = hit1->WireID().Cryostat;
937  unsigned int tpcIdx = hit1->WireID().TPC;
938 
939  // Initialize the wireIdVec
940  std::vector<geo::WireID> wireIDVec = {geo::WireID(cryostatIdx, tpcIdx, 0, 0),
941  geo::WireID(cryostatIdx, tpcIdx, 1, 0),
942  geo::WireID(cryostatIdx, tpcIdx, 2, 0)};
943 
944  wireIDVec[hit1->WireID().Plane] = hit1->WireID();
945  wireIDVec[hit2->WireID().Plane] = hit2->WireID();
946 
947  // For compiling at the moment
948  std::vector<float> hitDelTSigVec = {0., 0., 0.};
949 
950  hitDelTSigVec[hit1->WireID().Plane] = deltaPeakTime / sigmaPeakTime;
951  hitDelTSigVec[hit2->WireID().Plane] = deltaPeakTime / sigmaPeakTime;
952 
953  // Create the 3D cluster hit
954  hitPair.initialize(hitPairCntr,
955  statusBits,
956  position,
957  totalCharge,
958  avePeakTime,
959  deltaPeakTime,
960  sigmaPeakTime,
961  hitChiSquare,
962  0.,
963  0.,
964  0.,
965  0.,
966  hitVector,
967  hitDelTSigVec,
968  wireIDVec);
969 
970  result = true;
971  }
972  }
973  }
974 
975  // Send it back
976  return result;
977  }
978 
980  const reco::ClusterHit3D& pair,
981  const reco::ClusterHit2D* hit) const
982  {
983  // Assume failure
984  bool result(false);
985 
986  // We are going to force the wire pitch here, some time in the future we need to fix
987  static const double wirePitch =
988  0.5 * m_wirePitchScaleFactor * *std::max_element(m_wirePitch, m_wirePitch + 3);
989 
990  // Recover hit info
991  float hitTimeTicks = hit->getTimeTicks();
992  float hitSigma = hit->getHit()->RMS();
993 
994  // Special case check
995  if (hitSigma > 2. * hit->getHit()->PeakAmplitude())
996  hitSigma = 2. * hit->getHit()->PeakAmplitude();
997 
998  // Let's do a quick consistency check on the input hits to make sure we are in range...
999  // Require the W hit to be "in range" with the UV Pair
1000  if (fabs(hitTimeTicks - pair.getAvePeakTime()) <
1001  m_hitWidthSclFctr * (pair.getSigmaPeakTime() + hitSigma)) {
1002  // Check the distance from the point to the wire the hit is on
1003  float hitWireDist = DistanceFromPointToHitWire(pair.getPosition(), hit->WireID());
1004 
1005  if (m_outputHistograms) m_maxSideVecVec.push_back(hitWireDist);
1006 
1007  // Reject hits that are not within range
1008  if (hitWireDist < wirePitch) {
1009  if (m_outputHistograms) m_pairWireDistVec.push_back(hitWireDist);
1010 
1011  // Use the existing code to see the U and W hits are willing to pair with the V hit
1012  reco::ClusterHit3D pair0h;
1013  reco::ClusterHit3D pair1h;
1014 
1015  // Recover all the hits involved
1016  const reco::ClusterHit2DVec& pairHitVec = pair.getHits();
1017  const reco::ClusterHit2D* hit0 = pairHitVec[0];
1018  const reco::ClusterHit2D* hit1 = pairHitVec[1];
1019 
1020  if (!hit0)
1021  hit0 = pairHitVec[2];
1022  else if (!hit1)
1023  hit1 = pairHitVec[2];
1024 
1025  // If good pairs made here then we can try to make a triplet
1026  if (makeHitPair(pair0h, hit0, hit, m_hitWidthSclFctr) &&
1027  makeHitPair(pair1h, hit1, hit, m_hitWidthSclFctr)) {
1028  // Get a copy of the input hit vector (note the order is by plane - by definition)
1029  reco::ClusterHit2DVec hitVector = pair.getHits();
1030 
1031  // include the new hit
1032  hitVector[hit->WireID().Plane] = hit;
1033 
1034  // Set up to get average peak time, hitChiSquare, etc.
1035  unsigned int statusBits(0x7);
1036  float avePeakTime(0.);
1037  float weightSum(0.);
1038  float xPosition(0.);
1039 
1040  // And get the wire IDs
1041  std::vector<geo::WireID> wireIDVec = {geo::WireID(), geo::WireID(), geo::WireID()};
1042 
1043  // First loop through the hits to get WireIDs and calculate the averages
1044  for (size_t planeIdx = 0; planeIdx < 3; planeIdx++) {
1045  const reco::ClusterHit2D* hit2D = hitVector[planeIdx];
1046 
1047  wireIDVec[planeIdx] = hit2D->WireID();
1048 
1051 
1053 
1054  float hitRMS = hit2D->getHit()->RMS();
1055  float peakTime = hit2D->getTimeTicks();
1056 
1057  // Basically, allow the range to extend to the nearest end of the snippet
1058  if (hit2D->getHit()->DegreesOfFreedom() < 2)
1059  hitRMS = std::min(hit2D->getTimeTicks() - float(hit2D->getHit()->StartTick()),
1060  float(hit2D->getHit()->EndTick()) - hit2D->getTimeTicks());
1061 
1062  float weight = 1. / (hitRMS * hitRMS);
1063 
1064  avePeakTime += peakTime * weight;
1065  xPosition += hit2D->getXPosition() * weight;
1066  weightSum += weight;
1067  }
1068 
1069  avePeakTime /= weightSum;
1070  xPosition /= weightSum;
1071 
1072  Eigen::Vector2f pair0hYZVec(pair0h.getPosition()[1], pair0h.getPosition()[2]);
1073  Eigen::Vector2f pair1hYZVec(pair1h.getPosition()[1], pair1h.getPosition()[2]);
1074  Eigen::Vector2f pairYZVec(pair.getPosition()[1], pair.getPosition()[2]);
1075  Eigen::Vector3f position(xPosition,
1076  float((pairYZVec[0] + pair0hYZVec[0] + pair1hYZVec[0]) / 3.),
1077  float((pairYZVec[1] + pair0hYZVec[1] + pair1hYZVec[1]) / 3.));
1078 
1079  // Armed with the average peak time, now get hitChiSquare and the sig vec
1080  float hitChiSquare(0.);
1081  float sigmaPeakTime(std::sqrt(1. / weightSum));
1082  std::vector<float> hitDelTSigVec;
1083 
1084  for (const auto& hit2D : hitVector) {
1085  float hitRMS = hit2D->getHit()->RMS();
1086 
1087  // Basically, allow the range to extend to the nearest end of the snippet
1088  if (hit2D->getHit()->DegreesOfFreedom() < 2)
1089  hitRMS = std::min(hit2D->getTimeTicks() - float(hit2D->getHit()->StartTick()),
1090  float(hit2D->getHit()->EndTick()) - hit2D->getTimeTicks());
1091 
1092  float combRMS = std::sqrt(hitRMS * hitRMS - sigmaPeakTime * sigmaPeakTime);
1093  float peakTime = hit2D->getTimeTicks();
1094  float deltaTime = peakTime - avePeakTime;
1095  float hitSig = deltaTime / combRMS;
1096 
1097  hitChiSquare += hitSig * hitSig;
1098 
1099  hitDelTSigVec.emplace_back(std::fabs(hitSig));
1100  }
1101 
1102  if (m_outputHistograms) m_chiSquare3DVec.push_back(hitChiSquare);
1103 
1104  int lowMinIndex(std::numeric_limits<int>::max());
1105  int lowMaxIndex(std::numeric_limits<int>::min());
1106  int hiMinIndex(std::numeric_limits<int>::max());
1107  int hiMaxIndex(std::numeric_limits<int>::min());
1108 
1109  // First task is to get the min/max values for the common overlap region
1110  for (const auto& hit2D : hitVector) {
1111  float range = 2. * hit2D->getHit()->RMS();
1112 
1113  // Basically, allow the range to extend to the nearest end of the snippet
1114  if (hit2D->getHit()->DegreesOfFreedom() < 2)
1115  range = std::min(hit2D->getTimeTicks() - float(hit2D->getHit()->StartTick()),
1116  float(hit2D->getHit()->EndTick()) - hit2D->getTimeTicks());
1117 
1118  int hitStart = hit2D->getHit()->PeakTime() - range - 0.5;
1119  int hitStop = hit2D->getHit()->PeakTime() + range + 0.5;
1120 
1121  lowMinIndex = std::min(hitStart, lowMinIndex);
1122  lowMaxIndex = std::max(hitStart, lowMaxIndex);
1123  hiMinIndex = std::min(hitStop + 1, hiMinIndex);
1124  hiMaxIndex = std::max(hitStop + 1, hiMaxIndex);
1125  }
1126 
1127  // Keep only "good" hits...
1128  if (hitChiSquare < m_maxHit3DChiSquare && hiMinIndex > lowMaxIndex) {
1129  // One more pass through hits to get charge
1130  std::vector<float> chargeVec;
1131 
1132  for (const auto& hit2D : hitVector)
1133  chargeVec.push_back(chargeIntegral(hit2D->getHit()->PeakTime(),
1134  hit2D->getHit()->PeakAmplitude(),
1135  hit2D->getHit()->RMS(),
1136  lowMaxIndex,
1137  hiMinIndex));
1138 
1139  float totalCharge =
1140  std::accumulate(chargeVec.begin(), chargeVec.end(), 0.) / float(chargeVec.size());
1141  float overlapRange = float(hiMinIndex - lowMaxIndex);
1142  float overlapFraction = overlapRange / float(hiMaxIndex - lowMinIndex);
1143 
1144  // Set up to compute the charge asymmetry
1145  std::vector<float> smallestChargeDiffVec;
1146  std::vector<float> chargeAveVec;
1147  float smallestDiff(std::numeric_limits<float>::max());
1148  float maxDeltaPeak(0.);
1149  size_t chargeIndex(0);
1150 
1151  for (size_t idx = 0; idx < 3; idx++) {
1152  size_t leftIdx = (idx + 2) % 3;
1153  size_t rightIdx = (idx + 1) % 3;
1154 
1155  smallestChargeDiffVec.push_back(std::abs(chargeVec[leftIdx] - chargeVec[rightIdx]));
1156  chargeAveVec.push_back(float(0.5 * (chargeVec[leftIdx] + chargeVec[rightIdx])));
1157 
1158  if (smallestChargeDiffVec.back() < smallestDiff) {
1159  smallestDiff = smallestChargeDiffVec.back();
1160  chargeIndex = idx;
1161  }
1162 
1163  // Take opportunity to look at peak time diff
1164  float deltaPeakTime =
1165  hitVector[leftIdx]->getTimeTicks() - hitVector[rightIdx]->getTimeTicks();
1166 
1167  if (std::abs(deltaPeakTime) > maxDeltaPeak) maxDeltaPeak = std::abs(deltaPeakTime);
1168 
1169  if (m_outputHistograms) m_deltaTimeVec.push_back(deltaPeakTime);
1170  }
1171 
1172  float chargeAsymmetry = (chargeAveVec[chargeIndex] - chargeVec[chargeIndex]) /
1173  (chargeAveVec[chargeIndex] + chargeVec[chargeIndex]);
1174 
1175  // If this is true there has to be a negative charge that snuck in somehow
1176  if (chargeAsymmetry < -1. || chargeAsymmetry > 1.) {
1177  const geo::WireID& hitWireID = hitVector[chargeIndex]->WireID();
1178 
1179  std::cout << "============> Charge asymmetry out of range: " << chargeAsymmetry
1180  << " <============" << std::endl;
1181  std::cout << " hit C: " << hitWireID.Cryostat << ", TPC: " << hitWireID.TPC
1182  << ", Plane: " << hitWireID.Plane << ", Wire: " << hitWireID.Wire
1183  << std::endl;
1184  std::cout << " charge: " << chargeVec[0] << ", " << chargeVec[1] << ", "
1185  << chargeVec[2] << std::endl;
1186  std::cout << " index: " << chargeIndex << ", smallest diff: " << smallestDiff
1187  << std::endl;
1188  return result;
1189  }
1190 
1191  // Usurping "deltaPeakTime" to be the maximum pull
1192  float deltaPeakTime = *std::max_element(hitDelTSigVec.begin(), hitDelTSigVec.end());
1193 
1194  if (m_outputHistograms) {
1195  m_smallChargeDiffVec.push_back(smallestDiff);
1196  m_smallIndexVec.push_back(chargeIndex);
1197  m_maxPullVec.push_back(deltaPeakTime);
1198  m_qualityMetricVec.push_back(hitChiSquare);
1199  m_spacePointChargeVec.push_back(totalCharge);
1200  m_overlapFractionVec.push_back(overlapFraction);
1201  m_overlapRangeVec.push_back(overlapRange);
1202  m_maxDeltaPeakVec.push_back(maxDeltaPeak);
1203  m_hitAsymmetryVec.push_back(chargeAsymmetry);
1204  }
1205 
1206  // Try to weed out cases where overlap doesn't match peak separation
1207  if (maxDeltaPeak > overlapRange) return result;
1208 
1209  // Create the 3D cluster hit
1210  hitTriplet.initialize(0,
1211  statusBits,
1212  position,
1213  totalCharge,
1214  avePeakTime,
1215  deltaPeakTime,
1216  sigmaPeakTime,
1217  hitChiSquare,
1218  overlapFraction,
1219  chargeAsymmetry,
1220  0.,
1221  0.,
1222  hitVector,
1223  hitDelTSigVec,
1224  wireIDVec);
1225 
1226  result = true;
1227  }
1228  }
1229  }
1230  }
1231 
1232  // return success/fail
1233  return result;
1234  }
1235 
1237  float peakAmp,
1238  float peakSigma,
1239  int low,
1240  int hi) const
1241  {
1242  float integral(0);
1243 
1244  for (int sigPos = low; sigPos < hi; sigPos++) {
1245  float arg = (float(sigPos) - peakMean + 0.5) / peakSigma;
1246  integral += peakAmp * std::exp(-0.5 * arg * arg);
1247  }
1248 
1249  return integral;
1250  }
1251 
1253  const reco::ClusterHit3D& pair,
1254  size_t maxChanStatus,
1255  size_t minChanStatus) const
1256  {
1257  // Assume failure (most common result)
1258  bool result(false);
1259 
1260  const reco::ClusterHit2D* hit0 = pair.getHits()[0];
1261  const reco::ClusterHit2D* hit1 = pair.getHits()[1];
1262 
1263  size_t missPlane(2);
1264 
1265  // u plane hit is missing
1266  if (!hit0) {
1267  hit0 = pair.getHits()[2];
1268  missPlane = 0;
1269  }
1270  // v plane hit is missing
1271  else if (!hit1) {
1272  hit1 = pair.getHits()[2];
1273  missPlane = 1;
1274  }
1275 
1276  // Which plane is missing?
1277  geo::WireID wireID0 = hit0->WireID();
1278  geo::WireID wireID1 = hit1->WireID();
1279 
1280  // Ok, recover the wireID expected in the third plane...
1281  geo::WireID wireIn(wireID0.Cryostat, wireID0.TPC, missPlane, 0);
1282  geo::WireID wireID = NearestWireID(pair.getPosition(), wireIn);
1283 
1284  // There can be a round off issue so check the next wire as well
1285  bool wireStatus = m_channelStatus[wireID.Plane][wireID.Wire] < maxChanStatus &&
1286  m_channelStatus[wireID.Plane][wireID.Wire] >= minChanStatus;
1287  bool wireOneStatus = m_channelStatus[wireID.Plane][wireID.Wire + 1] < maxChanStatus &&
1288  m_channelStatus[wireID.Plane][wireID.Wire + 1] >= minChanStatus;
1289 
1290  // Make sure they are of at least the minimum status
1291  if (wireStatus || wireOneStatus) {
1292  // Sort out which is the wire we're dealing with
1293  if (!wireStatus) wireID.Wire += 1;
1294 
1295  // Want to refine position since we "know" the missing wire
1296  if (auto widIntersect0 = m_wireReadoutGeom->WireIDsIntersect(wireID0, wireID)) {
1297  if (auto widIntersect1 = m_wireReadoutGeom->WireIDsIntersect(wireID1, wireID)) {
1298  Eigen::Vector3f newPosition(
1299  pair.getPosition()[0], pair.getPosition()[1], pair.getPosition()[2]);
1300 
1301  newPosition[1] = (newPosition[1] + widIntersect0->y + widIntersect1->y) / 3.;
1302  newPosition[2] =
1303  (newPosition[2] + widIntersect0->z + widIntersect1->z - 2. * m_zPosOffset) / 3.;
1304 
1305  pairOut = pair;
1306  pairOut.setWireID(wireID);
1307  pairOut.setPosition(newPosition);
1308 
1313 
1316 
1317  result = true;
1318  }
1319  }
1320  }
1321 
1322  return result;
1323  }
1324 
1326  const Hit2DSet& hit2DSet,
1327  const reco::ClusterHit3D& pair,
1328  float pairDeltaTimeLimits) const
1329  {
1330  static const float minCharge(0.);
1331 
1332  const reco::ClusterHit2D* bestVHit(0);
1333 
1334  float pairAvePeakTime(pair.getAvePeakTime());
1335 
1336  // Idea is to loop through the input set of hits and look for the best combination
1337  for (const auto& hit2D : hit2DSet) {
1338  if (hit2D->getHit()->Integral() < minCharge) continue;
1339 
1340  float hitVPeakTime(hit2D->getTimeTicks());
1341  float deltaPeakTime(pairAvePeakTime - hitVPeakTime);
1342 
1343  if (deltaPeakTime > pairDeltaTimeLimits) continue;
1344 
1345  if (deltaPeakTime < -pairDeltaTimeLimits) break;
1346 
1347  pairDeltaTimeLimits = fabs(deltaPeakTime);
1348  bestVHit = hit2D;
1349  }
1350 
1351  return bestVHit;
1352  }
1353 
1355  const reco::ClusterHit3D& pair,
1356  float range) const
1357  {
1358  static const float minCharge(0.);
1359 
1360  int numberInRange(0);
1361  float pairAvePeakTime(pair.getAvePeakTime());
1362 
1363  // Idea is to loop through the input set of hits and look for the best combination
1364  for (const auto& hit2D : hit2DSet) {
1365  if (hit2D->getHit()->Integral() < minCharge) continue;
1366 
1367  float hitVPeakTime(hit2D->getTimeTicks());
1368  float deltaPeakTime(pairAvePeakTime - hitVPeakTime);
1369 
1370  if (deltaPeakTime > range) continue;
1371 
1372  if (deltaPeakTime < -range) break;
1373 
1374  numberInRange++;
1375  }
1376 
1377  return numberInRange;
1378  }
1379 
1380  geo::WireID StandardHit3DBuilder::NearestWireID(const Eigen::Vector3f& position,
1381  const geo::WireID& wireIDIn) const
1382  {
1383  geo::WireID wireID = wireIDIn;
1384 
1385  // Embed the call to the geometry's services nearest wire id method in a try-catch block
1386  try {
1387  // Switch from NearestWireID to this method to avoid the roundoff error issues...
1388  double distanceToWire =
1389  m_wireReadoutGeom->Plane(wireIDIn).WireCoordinate(geo::vect::toPoint(position.data()));
1390 
1391  wireID.Wire = int(distanceToWire);
1392  }
1393  catch (std::exception& exc) {
1394  // This can happen, almost always because the coordinates are **just** out of range
1395  mf::LogWarning("Cluster3D") << "Exception caught finding nearest wire, position - "
1396  << exc.what() << std::endl;
1397 
1398  // Assume extremum for wire number depending on z coordinate
1399  if (position[2] < 0.5 * m_geometry->TPC().Length())
1400  wireID.Wire = 0;
1401  else
1402  wireID.Wire = m_wireReadoutGeom->Nwires(wireIDIn.asPlaneID()) - 1;
1403  }
1404 
1405  return wireID;
1406  }
1407 
1408  float StandardHit3DBuilder::DistanceFromPointToHitWire(const Eigen::Vector3f& position,
1409  const geo::WireID& wireIDIn) const
1410  {
1411  float distance;
1412 
1413  // Embed the call to the geometry's services nearest wire id method in a try-catch block
1414  try {
1415  // Get the wire endpoints
1416  Eigen::Vector3d wireStart;
1417  Eigen::Vector3d wireEnd;
1418 
1419  m_wireReadoutGeom->WireEndPoints(wireIDIn, &wireStart[0], &wireEnd[0]);
1420 
1421  // Want the hit position to have same x value as wire coordinates
1422  Eigen::Vector3d hitPosition(wireStart[0], position[1], position[2]);
1423 
1424  // Want the wire direction
1425  Eigen::Vector3d wireDir = wireEnd - wireStart;
1426 
1427  wireDir.normalize();
1428 
1429  // Get arc length to doca
1430  double arcLen = (hitPosition - wireStart).dot(wireDir);
1431 
1432  Eigen::Vector3d docaVec = hitPosition - (wireStart + arcLen * wireDir);
1433 
1434  distance = docaVec.norm();
1435  }
1436  catch (std::exception& exc) {
1437  // This can happen, almost always because the coordinates are **just** out of range
1438  mf::LogWarning("Cluster3D") << "Exception caught finding nearest wire, position - "
1439  << exc.what() << std::endl;
1440 
1441  // Assume extremum for wire number depending on z coordinate
1442  distance = 0.;
1443  }
1444 
1445  return distance;
1446  }
1447 
1448  //------------------------------------------------------------------------------------------------------------------------------------------
1449  bool SetHitTimeOrder(const reco::ClusterHit2D* left, const reco::ClusterHit2D* right)
1450  {
1451  // Sort by "modified start time" of pulse
1452  return left->getHit()->PeakTime() < right->getHit()->PeakTime();
1453  }
1454 
1456  const reco::ClusterHit2D* right) const
1457  {
1458  return left->getHit()->PeakTime() < right->getHit()->PeakTime();
1459  }
1460 
1461  //------------------------------------------------------------------------------------------------------------------------------------------
1463  {
1468  // Start by getting a vector of valid, non empty hit collections to make sure we really have something to do here...
1469  // Here is a container for the hits...
1470  std::vector<const recob::Hit*> recobHitVec;
1471 
1472  auto const clock_data =
1474  auto const det_prop =
1476 
1477  // Loop through the list of input sources
1478  for (const auto& inputTag : m_hitFinderTagVec) {
1479  art::Handle<std::vector<recob::Hit>> recobHitHandle;
1480  evt.getByLabel(inputTag, recobHitHandle);
1481 
1482  if (!recobHitHandle.isValid() || recobHitHandle->size() == 0) continue;
1483 
1484  recobHitVec.reserve(recobHitVec.size() + recobHitHandle->size());
1485 
1486  for (const auto& hit : *recobHitHandle)
1487  recobHitVec.push_back(&hit);
1488  }
1489 
1490  // If the vector is empty there is nothing to do
1491  if (recobHitVec.empty()) return;
1492 
1493  cet::cpu_timer theClockMakeHits;
1494 
1495  if (m_enableMonitoring) theClockMakeHits.start();
1496 
1497  // We'll want to correct the hit times for the plane offsets
1498  // (note this is already taken care of when converting to position)
1499  std::map<geo::PlaneID, double> planeOffsetMap;
1500 
1501  // Try to output a formatted string
1502  std::string debugMessage("");
1503 
1504  // Initialize the plane to hit vector map
1505  for (size_t cryoIdx = 0; cryoIdx < m_geometry->Ncryostats(); cryoIdx++) {
1506  for (size_t tpcIdx = 0; tpcIdx < m_geometry->NTPC(); tpcIdx++) {
1507  m_planeToHitVectorMap[geo::PlaneID(cryoIdx, tpcIdx, 0)] = HitVector();
1508  m_planeToHitVectorMap[geo::PlaneID(cryoIdx, tpcIdx, 1)] = HitVector();
1509  m_planeToHitVectorMap[geo::PlaneID(cryoIdx, tpcIdx, 2)] = HitVector();
1510 
1511  // What we want here are the relative offsets between the planes
1512  // Note that plane 0 is assumed the "first" plane and is the reference
1513  planeOffsetMap[geo::PlaneID(cryoIdx, tpcIdx, 0)] = 0.;
1514  planeOffsetMap[geo::PlaneID(cryoIdx, tpcIdx, 1)] =
1515  det_prop.GetXTicksOffset(geo::PlaneID(cryoIdx, tpcIdx, 1)) -
1516  det_prop.GetXTicksOffset(geo::PlaneID(cryoIdx, tpcIdx, 0));
1517  planeOffsetMap[geo::PlaneID(cryoIdx, tpcIdx, 2)] =
1518  det_prop.GetXTicksOffset(geo::PlaneID(cryoIdx, tpcIdx, 2)) -
1519  det_prop.GetXTicksOffset(geo::PlaneID(cryoIdx, tpcIdx, 0));
1520 
1521  // Should we provide output?
1523  std::ostringstream outputString;
1524 
1525  outputString << "***> plane 0 offset: "
1526  << planeOffsetMap[geo::PlaneID(cryoIdx, tpcIdx, 0)]
1527  << ", plane 1: " << planeOffsetMap[geo::PlaneID(cryoIdx, tpcIdx, 1)]
1528  << ", plane 2: " << planeOffsetMap[geo::PlaneID(cryoIdx, tpcIdx, 2)]
1529  << "\n";
1530  debugMessage += outputString.str();
1531  outputString << " Det prop plane 0: "
1532  << det_prop.GetXTicksOffset(geo::PlaneID(cryoIdx, tpcIdx, 0))
1533  << ", plane 1: "
1534  << det_prop.GetXTicksOffset(geo::PlaneID(cryoIdx, tpcIdx, 1))
1535  << ", plane 2: "
1536  << det_prop.GetXTicksOffset(geo::PlaneID(cryoIdx, tpcIdx, 2))
1537  << ", Trig: " << trigger_offset(clock_data) << "\n";
1538  debugMessage += outputString.str();
1539  }
1540  }
1541  }
1542 
1544  mf::LogDebug("Cluster3D") << debugMessage << std::endl;
1545 
1547  }
1548 
1549  // Cycle through the recob hits to build ClusterHit2D objects and insert
1550  // them into the map
1551  for (const auto& recobHit : recobHitVec) {
1552  // Reject hits with negative charge, these are misreconstructed
1553  if (recobHit->Integral() < 0.) continue;
1554 
1555  // For some detectors we can have multiple wire ID's associated to a given channel.
1556  // So we recover the list of these wire IDs
1557  const std::vector<geo::WireID>& wireIDs =
1558  m_wireReadoutGeom->ChannelToWire(recobHit->Channel());
1559 
1560  // And then loop over all possible to build out our maps
1561  for (const auto& wireID : wireIDs) {
1562  // Check if this is an invalid TPC
1563  // (for example, in protoDUNE there are logical TPC's which see no signal)
1564  if (std::find(m_invalidTPCVec.begin(), m_invalidTPCVec.end(), wireID.TPC) !=
1565  m_invalidTPCVec.end())
1566  continue;
1567 
1568  // Note that a plane ID will define cryostat, TPC and plane
1569  const geo::PlaneID& planeID = wireID.planeID();
1570 
1571  double hitPeakTime(recobHit->PeakTime() - planeOffsetMap[planeID]);
1572  double xPosition(det_prop.ConvertTicksToX(
1573  recobHit->PeakTime(), planeID.Plane, planeID.TPC, planeID.Cryostat));
1574 
1575  m_clusterHit2DMasterList.emplace_back(0, 0., 0., xPosition, hitPeakTime, wireID, recobHit);
1576 
1577  m_planeToHitVectorMap[planeID].push_back(&m_clusterHit2DMasterList.back());
1578  m_planeToWireToHitSetMap[planeID][wireID.Wire].insert(&m_clusterHit2DMasterList.back());
1579  }
1580  }
1581 
1582  // Make a loop through to sort the recover hits in time order
1583  for (auto& hitVectorMap : m_planeToHitVectorMap)
1584  std::sort(hitVectorMap.second.begin(), hitVectorMap.second.end(), SetHitTimeOrder);
1585 
1586  if (m_enableMonitoring) {
1587  theClockMakeHits.stop();
1588 
1589  m_timeVector[COLLECTARTHITS] = theClockMakeHits.accumulated_real_time();
1590  }
1591 
1592  mf::LogDebug("Cluster3D") << ">>>>> Number of ART hits: " << m_clusterHit2DMasterList.size()
1593  << std::endl;
1594  }
1595 
1596  //------------------------------------------------------------------------------------------------------------------------------------------
1597 
1599  reco::HitPairList& hitPairList,
1600  std::vector<recob::Hit>& hitPtrVec,
1601  RecobHitToPtrMap& recobHitToPtrMap)
1602  {
1603  // Set up the timing
1604  cet::cpu_timer theClockBuildNewHits;
1605 
1606  if (m_enableMonitoring) theClockBuildNewHits.start();
1607 
1608  // We want to build a unique list of hits from the input 3D points which, we know, reuse 2D points frequently
1609  // At the same time we need to create a new 2D hit with the "correct" WireID and replace the old 2D hit in the
1610  // ClusterHit2D object with this new one... while keeping track of the use of the old ones. My head is spinning...
1611  // Declare a set which will allow us to keep track of those CusterHit2D objects we have seen already
1612  std::set<const reco::ClusterHit2D*> visitedHit2DSet;
1613 
1614  // Use this handy art utility to make art::Ptr objects to the new recob::Hits for use in the output phase
1615  art::PtrMaker<recob::Hit> ptrMaker(event);
1616 
1617  // Reserve enough memory to replace every recob::Hit we have considered (this is upper limit)
1618  hitPtrVec.reserve(m_clusterHit2DMasterList.size());
1619 
1620  // Scheme is to loop through all 3D hits, then through each associated ClusterHit2D object
1621  for (reco::ClusterHit3D& hit3D : hitPairList) {
1622  reco::ClusterHit2DVec& hit2DVec = hit3D.getHits();
1623 
1624  // The loop is over the index so we can recover the correct WireID to associate to the new hit when made
1625  for (size_t idx = 0; idx < hit3D.getHits().size(); idx++) {
1626  const reco::ClusterHit2D* hit2D = hit2DVec[idx];
1627 
1628  // Have we seen this 2D hit already?
1629  if (visitedHit2DSet.find(hit2D) == visitedHit2DSet.end()) {
1630  visitedHit2DSet.insert(hit2D);
1631 
1632  // Create and save the new recob::Hit with the correct WireID
1633  hitPtrVec.emplace_back(
1634  recob::HitCreator(*hit2D->getHit(), hit3D.getWireIDs()[idx]).copy());
1635 
1636  // Recover a pointer to it...
1637  recob::Hit* newHit = &hitPtrVec.back();
1638 
1639  // Create a mapping from this hit to an art Ptr representing it
1640  recobHitToPtrMap[newHit] = ptrMaker(hitPtrVec.size() - 1);
1641 
1642  // And set the pointer to this hit in the ClusterHit2D object
1643  const_cast<reco::ClusterHit2D*>(hit2D)->setHit(newHit);
1644  }
1645  }
1646  }
1647 
1648  size_t numNewHits = hitPtrVec.size();
1649 
1650  if (m_enableMonitoring) {
1651  theClockBuildNewHits.stop();
1652 
1653  m_timeVector[BUILDNEWHITS] = theClockBuildNewHits.accumulated_real_time();
1654  }
1655 
1656  mf::LogDebug("Cluster3D") << ">>>>> New output recob::Hit size: " << numNewHits << " (vs "
1657  << m_clusterHit2DMasterList.size() << " input)" << std::endl;
1658 
1659  return;
1660  }
1661 
1664  RecobHitToPtrMap& recobHitPtrMap) const
1665  {
1666  // Let's make sure the input associations container is empty
1668 
1669  // First task is to recover all of the previous wire <--> hit associations and map them by channel number
1670  // Create the temporary container
1671  std::unordered_map<raw::ChannelID_t, art::Ptr<recob::Wire>> channelToWireMap;
1672 
1673  // Go through the list of input sources and fill out the map
1674  for (const auto& inputTag : m_hitFinderTagVec) {
1676  evt.getValidHandle<std::vector<recob::Hit>>(inputTag);
1677 
1678  art::FindOneP<recob::Wire> hitToWireAssns(hitHandle, evt, inputTag);
1679 
1680  if (hitToWireAssns.isValid()) {
1681  for (size_t wireIdx = 0; wireIdx < hitToWireAssns.size(); wireIdx++) {
1682  art::Ptr<recob::Wire> wire = hitToWireAssns.at(wireIdx);
1683 
1684  channelToWireMap[wire->Channel()] = wire;
1685  }
1686  }
1687  }
1688 
1689  // Now fill the container
1690  for (const auto& hitPtrPair : recobHitPtrMap) {
1691  raw::ChannelID_t channel = hitPtrPair.first->Channel();
1692 
1693  std::unordered_map<raw::ChannelID_t, art::Ptr<recob::Wire>>::iterator chanWireItr =
1694  channelToWireMap.find(channel);
1695 
1696  if (!(chanWireItr != channelToWireMap.end())) {
1697  mf::LogDebug("Cluster3D") << "** Did not find channel to wire match! Skipping..."
1698  << std::endl;
1699  continue;
1700  }
1701 
1702  wireAssns.addSingle(chanWireItr->second, hitPtrPair.second);
1703  }
1704 
1705  return;
1706  }
1707 
1710  RecobHitToPtrMap& recobHitPtrMap) const
1711  {
1712  // Let's make sure the input associations container is empty
1713  rawDigitAssns = art::Assns<raw::RawDigit, recob::Hit>();
1714 
1715  // First task is to recover all of the previous wire <--> hit associations and map them by channel number
1716  // Create the temporary container
1717  std::unordered_map<raw::ChannelID_t, art::Ptr<raw::RawDigit>> channelToRawDigitMap;
1718 
1719  // Go through the list of input sources and fill out the map
1720  for (const auto& inputTag : m_hitFinderTagVec) {
1722  evt.getValidHandle<std::vector<recob::Hit>>(inputTag);
1723 
1724  art::FindOneP<raw::RawDigit> hitToRawDigitAssns(hitHandle, evt, inputTag);
1725 
1726  if (hitToRawDigitAssns.isValid()) {
1727  for (size_t rawDigitIdx = 0; rawDigitIdx < hitToRawDigitAssns.size(); rawDigitIdx++) {
1728  art::Ptr<raw::RawDigit> rawDigit = hitToRawDigitAssns.at(rawDigitIdx);
1729 
1730  channelToRawDigitMap[rawDigit->Channel()] = rawDigit;
1731  }
1732  }
1733  }
1734 
1735  // Now fill the container
1736  for (const auto& hitPtrPair : recobHitPtrMap) {
1737  raw::ChannelID_t channel = hitPtrPair.first->Channel();
1738 
1739  std::unordered_map<raw::ChannelID_t, art::Ptr<raw::RawDigit>>::iterator chanRawDigitItr =
1740  channelToRawDigitMap.find(channel);
1741 
1742  if (!(chanRawDigitItr != channelToRawDigitMap.end())) {
1743  mf::LogDebug("Cluster3D") << "** Did not find channel to wire match! Skipping..."
1744  << std::endl;
1745  continue;
1746  }
1747 
1748  rawDigitAssns.addSingle(chanRawDigitItr->second, hitPtrPair.second);
1749  }
1750 
1751  return;
1752  }
1753 
1754  //------------------------------------------------------------------------------------------------------------------------------------------
1755 
1757 } // namespace lar_cluster3d
void initialize(size_t id, unsigned int statusBits, const Eigen::Vector3f &position, float totalCharge, float avePeakTime, float deltaPeakTime, float sigmaPeakTime, float hitChiSquare, float overlapFraction, float chargeAsymmetry, float docaToAxis, float arclenToPoca, const ClusterHit2DVec &hitVec, const std::vector< float > &hitDelTSigVec, const std::vector< geo::WireID > &wireIDVec)
Definition: Cluster3D.cxx:156
intermediate_table::iterator iterator
bool makeHitTriplet(reco::ClusterHit3D &pairOut, const reco::ClusterHit3D &pairIn, const reco::ClusterHit2D *hit2) const
Make a 3D HitPair object by checking two hits.
void findGoodTriplets(HitMatchPairVecMap &, HitMatchPairVecMap &, reco::HitPairList &) const
This algorithm takes lists of hit pairs and finds good triplets.
float m_wirePitchScaleFactor
Scaling factor to determine max distance allowed between candidate pairs.
void setWireID(const geo::WireID &wid) const
Definition: Cluster3D.cxx:191
std::list< reco::ClusterHit3D > HitPairList
Definition: Cluster3D.h:330
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:102
#define DEFINE_ART_CLASS_TOOL(tool)
Definition: ToolMacros.h:42
unsigned int Nwires(PlaneID const &planeid) const
Returns the total number of wires in the specified plane.
void CreateNewRecobHitCollection(art::Event &, reco::HitPairList &, std::vector< recob::Hit > &, RecobHitToPtrMap &)
Create a new 2D hit collection from hits associated to 3D space points.
float getTimeTicks() const
Definition: Cluster3D.h:75
T * get() const
Definition: ServiceHandle.h:69
std::vector< size_t > ChannelStatusVec
define data structure for keeping track of channel status
double WireCoordinate(Point_t const &point) const
Returns the coordinate of the point on the plane, in wire units.
Definition: PlaneGeo.h:704
const Eigen::Vector3f getPosition() const
Definition: Cluster3D.h:155
bool SetHitTimeOrder(const reco::ClusterHit2D *left, const reco::ClusterHit2D *right)
float RMS() const
RMS of the hit shape, in tick units.
Definition: Hit.h:234
unsigned int NTPC(CryostatID const &cryoid=details::cryostat_zero) const
Returns the total number of TPCs in the specified cryostat.
Definition: GeometryCore.h:416
Declaration of signal hit object.
size_t BuildHitPairMapByTPC(PlaneHitVectorItrPairVec &planeHitVectorItrPairVec, reco::HitPairList &hitPairList) const
virtual unsigned int Nchannels() const =0
Returns the total number of channels present (not necessarily contiguous)
The data type to uniquely identify a Plane.
Definition: geo_types.h:364
const geo::WireID & WireID() const
Definition: Cluster3D.h:76
ChannelID_t Channel() const
DAQ channel this raw data was read from.
Definition: RawDigit.h:213
int findGoodHitPairs(const reco::ClusterHit2D *, HitVector::iterator &, HitVector::iterator &, HitMatchPairVecMap &) const
bool operator()(const reco::ClusterHit2D *, const reco::ClusterHit2D *) const
constexpr auto abs(T v)
Returns the absolute value of the argument.
int DegreesOfFreedom() const
Initial tdc tick for hit.
Definition: Hit.h:274
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:195
void BuildHit3D(reco::HitPairList &hitPairList) const
Given the ClusterHit2D objects, build the HitPairMap.
std::map< geo::WireID, HitMatchPairVec > HitMatchPairVecMap
cout<< "Opened file "<< fin<< " ixs= "<< ixs<< endl;if(ixs==0) hhh=(TH1F *) fff-> Get("h1")
Definition: AddMC.C:8
bool makeHitPair(reco::ClusterHit3D &pairOut, const reco::ClusterHit2D *hit1, const reco::ClusterHit2D *hit2, float hitWidthSclFctr=1., size_t hitPairCntr=0) const
Make a HitPair object by checking two hits.
void produces(art::ProducesCollector &) override
Each algorithm may have different objects it wants "produced" so use this to let the top level produc...
float Integral() const
Integral under the calibrated signal waveform of the hit, in tick x ADC units.
Definition: Hit.h:254
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:430
float getSigmaPeakTime() const
Definition: Cluster3D.h:162
void Hit3DBuilder(art::Event &, reco::HitPairList &, RecobHitToPtrMap &) override
Given a set of recob hits, run DBscan to form 3D clusters.
std::map< geo::PlaneID, WireToHitSetMap > PlaneToWireToHitSetMap
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
Definition: GeometryCore.h:303
bool SetPeakHitPairIteratorOrder(const reco::HitPairList::iterator &left, const reco::HitPairList::iterator &right)
const reco::ClusterHit2D * FindBestMatchingHit(const Hit2DSet &hit2DSet, const reco::ClusterHit3D &pair, float pairDeltaTimeLimits) const
A utility routine for finding a 2D hit closest in time to the given pair.
void BuildChannelStatusVec() const
Create the internal channel status vector (assume will eventually be event-by-event) ...
const recob::Hit * getHit() const
Definition: Cluster3D.h:77
float PeakAmplitude() const
The estimated amplitude of the hit at its peak, in ADC units.
Definition: Hit.h:238
double Length() const
Length is associated with z coordinate [cm].
Definition: TPCGeo.h:110
IDparameter< geo::PlaneID > PlaneID
Member type of validated geo::PlaneID parameter.
float m_maxHit3DChiSquare
Provide ability to select hits based on "chi square".
bool isValid() const noexcept
Definition: Handle.h:203
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Event.h:77
float getAvePeakTime() const
Definition: Cluster3D.h:160
std::map< size_t, HitVector > HitVectorMap
Class managing the creation of a new recob::Hit object.
Definition: HitCreator.h:87
void makeWireAssns(const art::Event &, art::Assns< recob::Wire, recob::Hit > &, RecobHitToPtrMap &) const
Create recob::Wire to recob::Hit associations.
geo::WireID NearestWireID(const Eigen::Vector3f &position, const geo::WireID &wireID) const
Jacket the calls to finding the nearest wire in order to intercept the exceptions if out of range...
Helper functions to create a hit.
std::vector< art::InputTag > m_hitFinderTagVec
Data members to follow.
void produces(std::string const &instanceName={}, Persistable const persistable=Persistable::Yes)
std::list< reco::ClusterHit2D > Hit2DList
IDparameter< geo::WireID > WireID
Member type of validated geo::WireID parameter.
bool m_outputHistograms
Take the time to create and fill some histograms for diagnostics.
std::map< geo::PlaneID, HitVector > PlaneToHitVectorMap
raw::ChannelID_t Channel() const
Returns the ID of the channel (or InvalidChannelID)
Definition: Wire.h:223
float chargeIntegral(float, float, float, int, int) const
Perform charge integration between limits.
Interface for a class providing readout channel mapping to geometry.
StandardHit3DBuilder class definiton.
int FindNumberInRange(const Hit2DSet &hit2DSet, const reco::ClusterHit3D &pair, float range) const
A utility routine for returning the number of 2D hits from the list in a given range.
const geo::WireReadoutGeom * m_wireReadoutGeom
Point_t toPoint(Point const &p)
Convert the specified point into a geo::Point_t.
void CollectArtHits(const art::Event &evt) const
Extract the ART hits and the ART hit-particle relationships.
float getXPosition() const
Definition: Cluster3D.h:74
TimeValues
enumerate the possible values for time checking if monitoring timing
Definition: IHit3DBuilder.h:56
The geometry of one entire detector, as served by art.
Definition: Geometry.h:42
The data type to uniquely identify a TPC.
Definition: geo_types.h:306
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:373
constexpr auto dot(Vector const &a, OtherVector const &b)
Return cross product of two vectors.
raw::TDCtick_t StartTick() const
Initial tdc tick for hit.
Definition: Hit.h:218
IHit3DBuilder interface class definiton.
Definition: IHit3DBuilder.h:27
Detector simulation of raw signals on wires.
bool makeDeadChannelPair(reco::ClusterHit3D &pairOut, const reco::ClusterHit3D &pair, size_t maxStatus, size_t minStatus) const
Make a 3D HitPair object from a valid pair and a dead channel in the missing plane.
float DistanceFromPointToHitWire(const Eigen::Vector3f &position, const geo::WireID &wireID) const
Jacket the calls to finding the nearest wire in order to intercept the exceptions if out of range...
void makeRawDigitAssns(const art::Event &, art::Assns< raw::RawDigit, recob::Hit > &, RecobHitToPtrMap &) const
Create raw::RawDigit to recob::Hit associations.
bool WireIDsIntersect(WireID const &wid1, WireID const &wid2, Point_t &intersection) const
Computes the intersection between two wires.
unsigned int Nplanes(TPCID const &tpcid=details::tpc_zero) const
Returns the total number of planes in the specified TPC.
const_iterator end() const
Definition: Assns.h:514
This provides an art tool interface definition for tools which construct 3D hits used in 3D clusterin...
std::vector< const reco::ClusterHit2D * > ClusterHit2DVec
Definition: Cluster3D.h:90
std::set< const reco::ClusterHit2D *, Hit2DSetCompare > Hit2DSet
raw::TDCtick_t EndTick() const
Final tdc tick for hit.
Definition: Hit.h:222
const lariov::ChannelStatusProvider * m_channelFilter
std::map< geo::TPCID, PlaneToWireToHitSetMap > TPCToPlaneToWireToHitSetMap
std::vector< const reco::ClusterHit2D * > HitVector
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:94
double weight
Definition: plottest35.C:25
bool operator()(const reco::HitPairClusterMap::iterator &left, const reco::HitPairClusterMap::iterator &right)
std::map< geo::TPCID, PlaneToHitVectorMap > TPCToPlaneToHitVectorMap
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:226
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
std::pair< const reco::ClusterHit2D *, reco::ClusterHit3D > HitMatchPair
This builds a list of candidate hit pairs from lists of hits on two planes.
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
void setID(const size_t &id) const
Definition: Cluster3D.h:176
virtual std::vector< WireID > ChannelToWire(raw::ChannelID_t channel) const =0
std::vector< ChannelStatusVec > ChannelStatusByPlaneVec
std::vector< std::pair< HitVector::iterator, HitVector::iterator >> PlaneHitVectorItrPairVec
Given the ClusterHit2D objects, build the HitPairMap.
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
void addSingle(Ptr< left_t > const &left, Ptr< right_t > const &right, data_t const &data)
Definition: Assns.h:549
size_t BuildHitPairMap(PlaneToHitVectorMap &planeToHitVectorMap, reco::HitPairList &hitPairList) const
Given the ClusterHit2D objects, build the HitPairMap.
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
int trigger_offset(DetectorClocksData const &data)
Definition: MVAAlg.h:12
StandardHit3DBuilder(fhicl::ParameterSet const &pset)
Constructor.
constexpr WireID()=default
Default constructor: an invalid TPC ID.
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:46
PlaneGeo const & Plane(TPCID const &tpcid, View_t view) const
Returns the specified wire.
TCEvent evt
Definition: DataStructs.cxx:8
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
TPCGeo const & TPC(TPCID const &tpcid=details::tpc_zero) const
Returns the specified TPC.
Definition: GeometryCore.h:448
second_as<> second
Type of time stored in seconds, in double precision.
Definition: spacetime.h:82
ROOT libraries.
float getTimeToExecute(IHit3DBuilder::TimeValues index) const override
If monitoring, recover the time to execute a particular function.
const ClusterHit2DVec & getHits() const
Definition: Cluster3D.h:168
void clear()
clear the tuple vectors before processing next event
std::unordered_map< const recob::Hit *, art::Ptr< recob::Hit >> RecobHitToPtrMap
Defines a structure mapping art representation to internal.
Definition: IHit3DBuilder.h:43
void WireEndPoints(WireID const &wireid, double *xyzStart, double *xyzEnd) const
Fills two arrays with the coordinates of the wire end points.
void setPosition(const Eigen::Vector3f &pos) const
Definition: Cluster3D.h:183
Definition: fwd.h:26
bool SetPairStartTimeOrder(const reco::ClusterHit3D &left, const reco::ClusterHit3D &right)
art framework interface to geometry description
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
vec_iX clear()
Event finding and building.
std::map< unsigned int, Hit2DSet > WireToHitSetMap
constexpr PlaneID const & asPlaneID() const
Conversion to WireID (for convenience of notation).
Definition: geo_types.h:466
unsigned getStatusBits() const
Definition: Cluster3D.h:71
void setStatusBit(unsigned bits) const
Definition: Cluster3D.h:79