LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
ClusterCrawlerAlg.cxx
Go to the documentation of this file.
1 
12 // C/C++ standard libraries
13 #include <cmath>
14 #include <iostream>
15 #include <iomanip>
16 #include <algorithm> // std::fill(), std::find(), std::sort()...
17 
18 // framework libraries
19 #include "fhiclcpp/ParameterSet.h"
22 
23 // LArSoft libraries
25 #include "larcorealg/CoreUtils/NumericUtils.h" // util::absDiff()
31 #include "larevt/CalibrationDBI/Interface/ChannelStatusService.h"
32 #include "larevt/CalibrationDBI/Interface/ChannelStatusProvider.h"
33 
34 struct CluLen{
35  int index;
36  int length;
37 };
38 
39 bool greaterThan (CluLen c1, CluLen c2) { return (c1.length > c2.length);}
40 bool lessThan (CluLen c1, CluLen c2) { return (c1.length < c2.length);}
41 
42 namespace cluster {
43 
44  //------------------------------------------------------------------------------
45  ClusterCrawlerAlg::ClusterCrawlerAlg(fhicl::ParameterSet const& pset)
46  {
47  reconfigure(pset);
48  }
49 
50 //------------------------------------------------------------------------------
51  void ClusterCrawlerAlg::reconfigure(fhicl::ParameterSet const& pset)
52  {
53 
54  fNumPass = pset.get< unsigned short >("NumPass", 0);
55  fMaxHitsFit = pset.get< std::vector<unsigned short> >("MaxHitsFit");
56  fMinHits = pset.get< std::vector<unsigned short> >("MinHits");
57  fNHitsAve = pset.get< std::vector<unsigned short> >("NHitsAve");
58  fChgCut = pset.get< std::vector<float> >("ChgCut");
59  fChiCut = pset.get< std::vector<float> >("ChiCut");
60  fMaxWirSkip = pset.get< std::vector<unsigned short> >("MaxWirSkip");
61  fMinWirAfterSkip = pset.get< std::vector<unsigned short> >("MinWirAfterSkip");
62  fKinkChiRat = pset.get< std::vector<float> >("KinkChiRat");
63  fKinkAngCut = pset.get< std::vector<float> >("KinkAngCut");
64  fDoMerge = pset.get< std::vector<bool> >("DoMerge");
65  fTimeDelta = pset.get< std::vector<float> >("TimeDelta");
66  fMergeChgCut = pset.get< std::vector<float> >("MergeChgCut");
67  fFindVertices = pset.get< std::vector<bool> >("FindVertices");
68  fLACrawl = pset.get< std::vector<bool> >("LACrawl");
69  fMinAmp = pset.get< std::vector<float> >("MinAmp", {5, 5, 5});
70  fChgNearWindow = pset.get< float >("ChgNearWindow");
71  fChgNearCut = pset.get< float >("ChgNearCut");
72 
73  fChkClusterDS = pset.get< bool >("ChkClusterDS",false);
74  fVtxClusterSplit = pset.get< bool >("VtxClusterSplit", false);
75  fFindStarVertices = pset.get< bool >("FindStarVertices", false);
76  if(pset.has_key("HammerCluster")) {
77  mf::LogWarning("CC")<<"fcl setting HammerCluster is replaced by FindHammerClusters. Ignoring...";
78  }
79  fFindHammerClusters = pset.get< bool >("FindHammerClusters", false);
80  fKillGarbageClusters = pset.get< float >("KillGarbageClusters", 0);
81  fRefineVertexClusters = pset.get< bool >("RefineVertexClusters", false);
82  fHitErrFac = pset.get< float >("HitErrFac", 0.2);
83  fHitMinAmp = pset.get< float >("HitMinAmp", 0.2);
84  fClProjErrFac = pset.get< float >("ClProjErrFac", 4);
85  fMinHitFrac = pset.get< float >("MinHitFrac", 0.6);
86 
87  fLAClusAngleCut = pset.get< float >("LAClusAngleCut", 45);
88  fLAClusMaxHitsFit = pset.get< unsigned short>("LAClusMaxHitsFit");
89  fMergeAllHits = pset.get< bool >("MergeAllHits", false);
90  fHitMergeChiCut = pset.get< float >("HitMergeChiCut", 2.5);
91  fMergeOverlapAngCut = pset.get< float >("MergeOverlapAngCut");
92  fAllowNoHitWire = pset.get< unsigned short >("AllowNoHitWire", 0);
93  fVertex2DCut = pset.get< float >("Vertex2DCut", 5);
94  fVertex2DWireErrCut = pset.get< float >("Vertex2DWireErrCut", 5);
95  fVertex3DCut = pset.get< float >("Vertex3DCut", 5);
96 
97  fDebugPlane = pset.get< int >("DebugPlane", -1);
98  fDebugWire = pset.get< int >("DebugWire", -1);
99  fDebugHit = pset.get< int >("DebugHit", -1);
100 
101 
102  // some error checking
103  bool badinput = false;
104  if(fNumPass > fMaxHitsFit.size()) badinput = true;
105  if(fNumPass > fMinHits.size()) badinput = true;
106  if(fNumPass > fNHitsAve.size()) badinput = true;
107  if(fNumPass > fChgCut.size()) badinput = true;
108  if(fNumPass > fChiCut.size()) badinput = true;
109  if(fNumPass > fMaxWirSkip.size()) badinput = true;
110  if(fNumPass > fMinWirAfterSkip.size()) badinput = true;
111  if(fNumPass > fKinkChiRat.size()) badinput = true;
112  if(fNumPass > fKinkAngCut.size()) badinput = true;
113  if(fNumPass > fDoMerge.size()) badinput = true;
114  if(fNumPass > fTimeDelta.size()) badinput = true;
115  if(fNumPass > fMergeChgCut.size()) badinput = true;
116  if(fNumPass > fFindVertices.size()) badinput = true;
117  if(fNumPass > fLACrawl.size()) badinput = true;
118 
119  if(badinput) throw art::Exception(art::errors::Configuration)
120  << "ClusterCrawlerAlg: Bad input from fcl file";
121 
122 
123  } // reconfigure
124 
125 
126  // used for sorting hits on wires
127  bool SortByLowHit(unsigned int i, unsigned int j) {return ((i > j));}
128 
129  bool ClusterCrawlerAlg::SortByMultiplet(recob::Hit const& a, recob::Hit const& b)
130  {
131  // compare the wire IDs first:
132  int cmp_res = a.WireID().cmp(b.WireID());
133  if (cmp_res != 0) return cmp_res < 0; // order is decided, unless equal
134  // decide by start time
135  if (a.StartTick() != b.StartTick()) return a.StartTick() < b.StartTick();
136  // if still undecided, resolve by local index
137  return a.LocalIndex() < b.LocalIndex(); // if still unresolved, it's a bug!
138  } // ClusterCrawlerAlg::SortByMultiplet()
139 
140 
141 //------------------------------------------------------------------------------
142  void ClusterCrawlerAlg::ClearResults() {
143  fHits.clear();
144  tcl.clear();
145  vtx.clear();
146  vtx3.clear();
147  inClus.clear();
148  } // ClusterCrawlerAlg::ClearResults()
149 
150 
151 //------------------------------------------------------------------------------
152  void ClusterCrawlerAlg::CrawlInit() {
153  prt = false; vtxprt = false;
154  NClusters = 0; clBeginSlp = 0; clBeginSlpErr = 0; clBeginTim = 0;
155  clBeginWir = 0; clBeginChg = 0; clBeginChgNear = 0; clEndSlp = 0; clEndSlpErr = 0;
156  clEndTim = 0; clEndWir = 0; clEndChg = 0; clEndChgNear = 0; clChisq = 0;
157  clStopCode = 0; clProcCode = 0; fFirstWire = 0;
158  fLastWire = 0; fAveChg = 0.; fChgSlp = 0.; pass = 0;
159  fScaleF = 0; WireHitRange.clear();
160  // unMergedHits.clear();
161 
162  ClearResults();
163  }
164 
165  //------------------------------------------------------------------------------
166  void ClusterCrawlerAlg::ClusterInit()
167  {
168  fcl2hits.clear();
169  chifits.clear();
170  hitNear.clear();
171  chgNear.clear();
172  fAveChg = -1.;
173  fAveHitWidth = -1;
174 // unMergedHits.clear();
175  clEndChg = -1.;
176  clStopCode = 0;
177  clProcCode = pass;
178  }
179 
180 //------------------------------------------------------------------------------
181  void ClusterCrawlerAlg::RunCrawler(std::vector<recob::Hit> const& srchits)
182  {
183  // Run the ClusterCrawler algorithm - creating seed clusters and crawling upstream.
184 
185  CrawlInit();
186 
187  fHits = srchits; // plain copy of the sources; it's the base of our hit result
188 
189  if(fHits.size() < 3) return;
190  if(fHits.size() > UINT_MAX) {
191  mf::LogWarning("CC")<<"Too many hits for ClusterCrawler "<<fHits.size();
192  return;
193  }
194 
195  // don't do anything...
196  if(fNumPass == 0) return;
197 
198  // sort it as needed;
199  // that is, sorted by wire ID number,
200  // then by start of the region of interest in time, then by the multiplet
201  std::sort(fHits.begin(), fHits.end(), &SortByMultiplet);
202 
203  inClus.resize(fHits.size());
204  mergeAvailable.resize(fHits.size());
205  for(unsigned int iht = 0; iht < inClus.size(); ++iht) {
206  inClus[iht] = 0;
207  mergeAvailable[iht] = false;
208  }
209 
210  const detinfo::DetectorProperties* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
211 
212  for (geo::TPCID const& tpcid: geom->IterateTPCIDs()) {
213  geo::TPCGeo const& TPC = geom->TPC(tpcid);
214  for(plane = 0; plane < TPC.Nplanes(); ++plane){
215  WireHitRange.clear();
216  // define a code to ensure clusters are compared within the same plane
217  clCTP = EncodeCTP(tpcid.Cryostat, tpcid.TPC, plane);
218  cstat = tpcid.Cryostat;
219  tpc = tpcid.TPC;
220  // fill the WireHitRange vector with first/last hit on each wire
221  // dead wires and wires with no hits are flagged < 0
222  GetHitRange(clCTP);
223 
224 // sanity check
225 /*
226  std::cout<<"Plane "<<plane<<" sanity check. Wire range "<<fFirstWire<<" "<<fLastWire;
227  unsigned int nhts = 0;
228  for(unsigned int wire = fFirstWire; wire < fLastWire; ++wire) {
229  if(WireHitRange[wire].first < 0) continue;
230  unsigned int fhit = WireHitRange[wire].first;
231  unsigned int lhit = WireHitRange[wire].second;
232  for(unsigned int hit = fhit; hit < lhit; ++hit) {
233  ++nhts;
234  if(fHits[hit].WireID().Wire != wire) {
235  std::cout<<"Bad wire "<<hit<<" "<<fHits[hit].WireID().Wire<<" "<<wire<<"\n";
236  return;
237  } // check wire
238  if(fHits[hit].WireID().Plane != plane) {
239  std::cout<<"Bad plane "<<hit<<" "<<fHits[hit].WireID().Plane<<" "<<plane<<"\n";
240  return;
241  } // check plane
242  if(fHits[hit].WireID().TPC != tpc) {
243  std::cout<<"Bad tpc "<<hit<<" "<<fHits[hit].WireID().TPC<<" "<<tpc<<"\n";
244  return;
245  } // check tpc
246  } // hit
247  } // wire
248  std::cout<<" is OK. nhits "<<nhts<<"\n";
249 */
250  if (WireHitRange.empty()||(fFirstWire == fLastWire)) continue;
251  raw::ChannelID_t channel = fHits[fFirstHit].Channel();
252  // get the scale factor to convert dTick/dWire to dX/dU. This is used
253  // to make the kink and merging cuts
254  float wirePitch = geom->WirePitch(geom->View(channel));
255  float tickToDist = detprop->DriftVelocity(detprop->Efield(),detprop->Temperature());
256  tickToDist *= 1.e-3 * detprop->SamplingRate(); // 1e-3 is conversion of 1/us to 1/ns
257  fScaleF = tickToDist / wirePitch;
258  // convert Large Angle Cluster crawling cut to a slope cut
259  if(fLAClusAngleCut > 0)
260  fLAClusSlopeCut = std::tan(3.142 * fLAClusAngleCut / 180.) / fScaleF;
261  fMaxTime = detprop->NumberTimeSamples();
262  fNumWires = geom->Nwires(plane, tpc, cstat);
263  // look for clusters
264  if(fNumPass > 0) ClusterLoop();
265  } // plane
266  if(fVertex3DCut > 0) {
267  // Match vertices in 3 planes
268  VtxMatch(tpcid);
269  Vtx3ClusterMatch(tpcid);
270  if(fFindHammerClusters) FindHammerClusters();
271  // split clusters using 3D vertices
272  Vtx3ClusterSplit(tpcid);
273  }
274  if(fDebugPlane >= 0) {
275  mf::LogVerbatim("CC")<<"Clustering done in TPC ";
276  PrintClusters();
277  }
278  } // for all tpcs
279 
280  // clean up
281  WireHitRange.clear();
282  fcl2hits.clear();
283  chifits.clear();
284  hitNear.clear();
285  chgNear.clear();
286 
287 /*
288  // TEMP. Print out cluster info for tuning
289  std::array<unsigned int, 3> ncl;
290  std::array<unsigned int, 3> nht;
291  std::array<unsigned int, 3> nhtTot;
292  std::array<unsigned int, 3> nhtNotMerged;
293  unsigned int ipl;
294  for(ipl = 0; ipl < 3; ++ipl) {
295  ncl[ipl] = 0;
296  nht[ipl] = 0;
297  nhtTot[ipl] = 0;
298  nhtNotMerged[ipl] = 0;
299  }
300  // hits in clusters
301  for(unsigned int icl = 0; icl < tcl.size(); ++icl) {
302  if(tcl[icl].ID < 0) continue;
303  geo::PlaneID iplID = DecodeCTP(tcl[icl].CTP);
304  ipl = iplID.Plane;
305  ++ncl[ipl];
306  nht[ipl] += tcl[icl].tclhits.size();
307  }
308  // total number of hits
309  for(unsigned int iht = 0; iht < fHits.size(); ++iht) {
310  ipl = fHits[iht].WireID().Plane;
311  ++nhtTot[ipl];
312  if(inClus[iht] >= 0) ++nhtNotMerged[ipl];
313  }
314  int frc1, frc2;
315  for(ipl = 0; ipl < 3; ++ipl) {
316  frc1 = 0; frc2 = 0;
317  if(nhtNotMerged[ipl] > 0) frc1 = 100 * (float)nht[ipl] / (float)nhtNotMerged[ipl];
318  if(nhtTot[ipl] > 0) frc2 = 100 * (float)nhtNotMerged[ipl] / (float)nhtTot[ipl];
319  frc2 = 100 - frc2;
320  std::cout<<"plane "<<ipl<<" num clusters "<<ncl[ipl]<<" Hits in clusters "<<frc1<<"%. Hits merged "<<frc2<<"%\n";
321  }
322 */
323  // remove the hits that have become obsolete
324  RemoveObsoleteHits();
325 
326  } // RunCrawler
327 
329  void ClusterCrawlerAlg::ClusterLoop()
330  {
331  // looks for seed clusters in a plane and crawls along a trail of hits
332 
333  unsigned int nHitsUsed = 0, iwire, jwire, kwire;
334  bool AllDone = false, SigOK = false, HitOK = false;
335  unsigned int ihit, jhit;
336  for(unsigned short thispass = 0; thispass < fNumPass; ++thispass) {
337  pass = thispass;
338  // look for a starting cluster that spans a block of wires
339  unsigned int span = 3;
340  if(fMinHits[pass] < span) span = fMinHits[pass];
341  for(iwire = fLastWire; iwire > fFirstWire + span; --iwire) {
342  // skip bad wires or no hits on the wire
343  if(WireHitRange[iwire].first < 0) continue;
344  auto ifirsthit = (unsigned int)WireHitRange[iwire].first;
345  auto ilasthit = (unsigned int)WireHitRange[iwire].second;
346  for(ihit = ifirsthit; ihit < ilasthit; ++ihit) {
347  bool ClusterAdded = false;
348  recob::Hit const& hit = fHits[ihit];
349  // skip used hits
350  if(ihit > fHits.size()-1) {
351  mf::LogError("CC")<<"ClusterLoop bad ihit "<<ihit<<" fHits size "<<fHits.size();
352  return;
353  }
354  // skip used and obsolete hits
355  if(inClus[ihit] != 0) continue;
356  // skip narrow hits
357  if(fHits[ihit].PeakAmplitude() < fHitMinAmp) continue;
358 // prt = (fDebugPlane == (int)plane && (int)iwire == fDebugWire && std::abs((int)hit.PeakTime() - fDebugHit) < 20);
359  if((iwire + 1) < span) continue;
360  jwire = iwire - span + 1;
361  if(prt) mf::LogVerbatim("CC")<<"Found debug hit "<<PrintHit(ihit)<<" on pass"<<pass;
362  // skip if good wire and no hit
363  if(WireHitRange[jwire].first == -2) continue;
364  if(WireHitRange[jwire].first == -1) {
365  // Found a dead jwire. Keep looking upstream until we find a good wire
366  unsigned int nmissed = 0;
367  while(WireHitRange[jwire].first == -1 && jwire > 1 && nmissed < fMaxWirSkip[pass]) {
368  --jwire;
369  ++nmissed;
370  }
371  if(prt) mf::LogVerbatim("CC")<<" new jwire "<<jwire<<" dead? "<<WireHitRange[jwire].first;
372  if(WireHitRange[jwire].first < 0) continue;
373  } // dead jwire
374  // Find the hit on wire jwire that best matches a line between
375  // a nearby vertex and hit ihit. No constraint if useHit < 0
376  unsigned int useHit = 0;
377  bool doConstrain = false;
378  VtxConstraint(iwire, ihit, jwire, useHit, doConstrain);
379  unsigned int jfirsthit = (unsigned int)WireHitRange[jwire].first;
380  unsigned int jlasthit = (unsigned int)WireHitRange[jwire].second;
381  if(jfirsthit > fHits.size()-1 || jfirsthit > fHits.size()-1) throw art::Exception(art::errors::LogicError)
382  <<"ClusterLoop jwire "<<jwire<<" bad firsthit "<<jfirsthit<<" lasthit "<<jlasthit<<" fhits size "<<fHits.size();
383  // if(prt) mf::LogVerbatim("CC")<<" jhit range "<<jfirsthit<<" "<<jlasthit;
384  for(jhit = jfirsthit; jhit < jlasthit; ++jhit) {
385  if(jhit > fHits.size()-1) throw art::Exception(art::errors::LogicError)
386  <<"ClusterLoop bad jhit "<<jhit<<" firsthit "<<jfirsthit<<" lasthit "<<jlasthit<<" fhits size"<<fHits.size();
387  // Constraint?
388  if(doConstrain && jhit != useHit) continue;
389  recob::Hit const& other_hit = fHits[jhit];
390  // skip used and obsolete hits
391  if(inClus[jhit] != 0) continue;
392  // skip narrow hits
393  if(fHits[jhit].PeakAmplitude() < fHitMinAmp) continue;
394  // start a cluster with these two hits
395  ClusterInit();
396  fcl2hits.push_back(ihit);
397  chifits.push_back(0.);
398  hitNear.push_back(0);
399  chgNear.push_back(0); // These will be defined if the cluster survives the cuts
400  // enter the jhit
401  fcl2hits.push_back(jhit);
402  chifits.push_back(0.);
403  hitNear.push_back(0);
404  chgNear.push_back(0);
405  clLA = false;
406  clpar[0] = other_hit.PeakTime();
407  clpar[1] = (hit.PeakTime() - other_hit.PeakTime()) / (iwire - jwire);
408  // increase slope errors for large angle clusters
409  clparerr[1] = 0.2 * std::abs(clpar[1]);
410  clpar[2] = fHits[jhit].WireID().Wire;
411  clChisq = 0;
412  // now look for hits to add on the intervening wires
413  bool clok = false;
414  for(kwire = jwire+1; kwire < iwire; ++kwire) {
415  // ensure this cluster doesn't cross a vertex
416  if(CrawlVtxChk(kwire)) {
417  clok = false;
418  break;
419  }
420  AddHit(kwire, HitOK, SigOK);
421  if(prt) mf::LogVerbatim("CC")<<" HitOK "<<HitOK<<" clChisq "<<clChisq<<" cut "<<fChiCut[pass]<<" ClusterHitsOK "<<ClusterHitsOK(-1);
422  // No hit found
423  if(!HitOK) break;
424  // This should be a really good chisq
425  if(clChisq > 2) break;
426  // hit widths & overlap not consistent
427  if(!ClusterHitsOK(-1)) continue;
428  clok = true;
429  }
430  // drop it?
431  if(!clok) continue;
432  prt = (fDebugPlane == (int)plane && (int)iwire == fDebugWire && std::abs((int)hit.PeakTime() - fDebugHit) < 20);
433  if(prt) mf::LogVerbatim("CC")<<"ADD >>>>> Starting cluster with hits "<<PrintHit(fcl2hits[0])<<" "<<PrintHit(fcl2hits[1])<<" "<<PrintHit(fcl2hits[2])<<" on pass "<<pass;
434  // save the cluster begin info
435  clBeginWir = iwire;
436  clBeginTim = hit.PeakTime();
437  clBeginSlp = clpar[1];
438  // don't do a small angle crawl if the cluster slope is too large
439  // and Large Angle crawling is NOT requested on this pass
440  if(!fLACrawl[pass] && std::abs(clBeginSlp) > fLAClusSlopeCut) continue;
441  // See if we are trying to start a cluster between a vertex
442  // and a cluster that is associated to that vertex. If so, skip it
443  if(CrawlVtxChk2()) continue;
444  clBeginSlpErr = clparerr[1];
445  clBeginChg = 0;
446  // Calculate the average width
447  fAveHitWidth = 0;
448  float chg = 0;
449  for(unsigned short kk = 0; kk < fcl2hits.size(); ++kk) {
450  fAveHitWidth += fHits[fcl2hits[kk]].EndTick() - fHits[fcl2hits[kk]].StartTick();
451  chg += fHits[fcl2hits[kk]].Integral();
452  }
453  fAveHitWidth /= (float)fcl2hits.size();
454  // decide whether to crawl a large angle cluster. Requirements are:
455  // 1) the user has set the LACluster angle cut > 0, AND
456  // 2) the cluster slope exceeds the cut
457  // Note that if condition 1 is met, normal cluster crawling is done
458  // only if the slope is less than the cut
459  if(fLACrawl[pass] && fLAClusSlopeCut > 0) {
460  // LA cluster crawling requested
461  if(std::abs(clBeginSlp) > fLAClusSlopeCut) {
462  LACrawlUS();
463  } else {
464  CrawlUS();
465  } // std::abs(clBeginSlp) > fLAClusAngleCut
466  } else {
467  // allow clusters of any angle
468  CrawlUS();
469  } // fLAClusSlopeCut > 0
470  if(fcl2hits.size() >= fMinHits[pass]) {
471  // it's long enough so save it
472  clEndSlp = clpar[1]; // save the slope at the end
473  clEndSlpErr = clparerr[1];
474  // store the cluster
475  if(!TmpStore()) {
476  mf::LogError("CC")<<"Failed to store cluster in plane "<<plane;
477  continue;
478  }
479  ClusterAdded = true;
480  nHitsUsed += fcl2hits.size();
481  AllDone = (nHitsUsed == fHits.size());
482  break;
483  } else {
484  // abandon it
485  if(prt) mf::LogVerbatim("CC")<<"ClusterLoop: dropped the cluster";
486  }
487  if(ClusterAdded || AllDone) break;
488  } // jhit
489  if(AllDone) break;
490  } // ihit
491  if(AllDone) break;
492  } // iwire
493 
494  // try to merge clusters
495  if(fDoMerge[pass]) ChkMerge();
496  // form 2D vertices
497  if(fFindVertices[pass]) FindVertices();
498 
499  if(AllDone) break;
500 
501  } // pass
502 
503  // Kill Garbage clusters
504  if(fKillGarbageClusters > 0 && !tcl.empty()) KillGarbageClusters();
505  // Merge overlapping clusters
506  if(fMergeOverlapAngCut > 0) MergeOverlap();
507  // Check the DS end of clusters
508  if(fChkClusterDS) ChkClusterDS();
509  // split clusters using vertices
510  if(fVtxClusterSplit) {
511  bool didSomething = VtxClusterSplit();
512  // iterate once to handle the case where a cluster crosses two vertices
513  if(didSomething) VtxClusterSplit();
514  }
515  // Look for 2D vertices with star topology - short, back-to-back clusters
516  if(fFindStarVertices) FindStarVertices();
517 
518  if(fDebugPlane == (int)plane) {
519  mf::LogVerbatim("CC")<<"Clustering done in plane "<<plane;
520  PrintClusters();
521  }
522 
523 // if(unMergedHits.size() > 0) mf::LogError("CC")<<"Found unMergedHits > 0 after post-pass processing";
524 
525  CheckHitClusterAssociations();
526 
527  } // ClusterLoop()
528 
530  void ClusterCrawlerAlg::KillGarbageClusters()
531  {
532  // Ghost Clusters:
533 
534  if(tcl.size() < 2) return;
535 
536  unsigned short icl, jcl;
537  // This code preferentially selects icl clusters that were
538  // reconstructed on an early pass (unless they were split)
539  std::vector<float> iHits, jHits;
540  unsigned int indx;
541  // find the average hit width on the first pass and construct
542  // a hit separation cut
543  float sepcut = 0, iFrac, jFrac;
544  bool first = true;
545  unsigned short iLoIndx, jLoIndx, olapSize, iop, ii, jj;
546  unsigned short nclose;
547  float iChg, jChg;
548  // vecrtor of clusters that will be killed after all is done
549  std::vector<unsigned short> killMe;
550  bool doKill;
551  for(icl = 0; icl < tcl.size() - 1; ++icl) {
552  if(tcl[icl].ID < 0) continue;
553  if(tcl[icl].CTP != clCTP) continue;
554 // if(tcl[icl].ProcCode < 300) continue;
555  // put the hits into a wire ordered vector
556  iHits.clear();
557  // initialize to a large positive value
558  iHits.resize(tcl[icl].BeginWir - tcl[icl].EndWir + 1, 1000);
559  iChg = 0;
560  for(auto iht : tcl[icl].tclhits) {
561  indx = fHits[iht].WireID().Wire - tcl[icl].EndWir;
562  if(indx > iHits.size() - 1) {
563  mf::LogWarning("CC")<<"KillGarbageClusters: icl ID "<<tcl[icl].ID<<" Bad indx "<<indx<<" "<<iHits.size()<<"\n";
564  continue;
565  }
566  iHits[indx] = fHits[iht].PeakTime();
567  iChg += fHits[iht].Integral();
568  if(first) sepcut += fHits[iht].RMS();
569  } // iht
570  if(first) {
571  sepcut /= (float)tcl[icl].tclhits.size();
572  // clusters are consider ghost candidates if many hits
573  // are within sepcut of each other on the same wire
574  sepcut *= 10;
575  first = false;
576  } // first
577  for(jcl = icl + 1; jcl < tcl.size(); ++jcl) {
578  if(tcl[jcl].ID < 0) continue;
579  if(tcl[jcl].CTP != clCTP) continue;
580 // if(tcl[jcl].ProcCode < 300) continue;
581  // ignore if there is no overlap
582  if(tcl[icl].BeginWir < tcl[jcl].EndWir) continue;
583  if(tcl[icl].EndWir > tcl[jcl].BeginWir) continue;
584  // require similar angle
585  if(std::abs(tcl[icl].BeginAng - tcl[jcl].BeginAng) > fKillGarbageClusters) continue;
586  // find the overlap region
587  if(tcl[icl].EndWir < tcl[jcl].EndWir) {
588  // icl E-----------....
589  // jcl E----------....
590  // olap xxxxxxxxxx...
591  iLoIndx = tcl[jcl].EndWir - tcl[icl].EndWir;
592  jLoIndx = 0;
593  if(tcl[icl].BeginWir < tcl[jcl].BeginWir) {
594  // icl E-----------B
595  // jcl E------------B
596  // olap xxxxxxxxxx
597  olapSize = tcl[icl].BeginWir - tcl[jcl].EndWir + 1;
598  } else {
599  // icl E-----------B
600  // jcl E-----B
601  // olap xxxxxxx
602  olapSize = tcl[jcl].BeginWir - tcl[jcl].EndWir + 1;
603  } // iBegin
604  } // iEnd < jEnd
605  else {
606  // icl E-----------....
607  // jcl E----------....
608  // olap xxxxxxxxxx...
609  iLoIndx = 0;
610  jLoIndx = tcl[icl].EndWir - tcl[icl].EndWir;
611  if(tcl[icl].BeginWir < tcl[jcl].BeginWir) {
612  // icl E-----B
613  // jcl E-----------B
614  // olap xxxxxxx
615  olapSize = tcl[icl].BeginWir - tcl[icl].EndWir + 1;
616  } else {
617  // icl E-----------B
618  // jcl E----------B
619  // olap xxxxxxxxx
620  olapSize = tcl[jcl].BeginWir - tcl[icl].EndWir + 1;
621  }
622  } // iEnd > jEnd
623  jHits.clear();
624  // initialize to a large negative value
625  jHits.resize(tcl[jcl].BeginWir - tcl[jcl].EndWir + 1, -1000);
626  jChg = 0;
627  for(auto jht : tcl[jcl].tclhits) {
628  indx = fHits[jht].WireID().Wire - tcl[jcl].EndWir;
629  if(indx > jHits.size() - 1) {
630  mf::LogWarning("CC")<<"KillGarbageClusters: jcl ID "<<tcl[jcl].ID<<" Bad indx "<<indx<<" "<<jHits.size()<<"\n";
631  continue;
632  }
633  jHits[indx] = fHits[jht].PeakTime();
634  jChg += fHits[jht].Integral();
635  } // jht
636 /*
637  mf::LogVerbatim myprt("CC");
638  myprt<<"Check icl ID "<<tcl[icl].ID<<" size "<<iHits.size()<<" iLoIndx "<<iLoIndx;
639  myprt<<" wire range "<<tcl[icl].EndWir<<"-"<<tcl[icl].BeginWir;
640  myprt<<" jcl ID "<<tcl[jcl].ID<<" size "<<jHits.size()<<" jLoIndx "<<jLoIndx;
641  myprt<<" wire range "<<tcl[jcl].EndWir<<"-"<<tcl[jcl].BeginWir;
642  myprt<<" olapSize "<<olapSize;
643 */
644  // count the number of close hits
645  nclose = 0;
646  for(iop = 0; iop < olapSize; ++iop) {
647  ii = iLoIndx + iop;
648  if(ii > iHits.size() - 1) continue;
649  jj = jLoIndx + iop;
650  if(jj > jHits.size() - 1) continue;
651  if(std::abs(iHits[ii] - jHits[jj]) < sepcut) ++nclose;
652  } // iop
653  iFrac = (float)nclose / (float)iHits.size();
654  jFrac = (float)nclose / (float)jHits.size();
655  if(iFrac < 0.5 && jFrac < 0.5) continue;
656  doKill = (iFrac < jFrac && iChg > jChg);
657 /*
658  mf::LogVerbatim myprt("CC");
659  myprt<<"Check icl ID "<<tcl[icl].ID<<" size "<<iHits.size()<<" iFrac "<<iFrac<<" iChg "<<(int)iChg;
660  myprt<<" jcl ID "<<tcl[jcl].ID<<" size "<<jHits.size()<<" jFrac "<<jFrac<<" jChg "<<(int)jChg<<" doKill? "<<doKill;
661 */
662  if(doKill) killMe.push_back(jcl);
663  } // jcl
664  } // icl
665 
666  if(killMe.size() == 0) return;
667  for(auto icl : killMe) {
668  // killing time
669  if(tcl[icl].ID < 0) continue;
670  tcl[icl].ProcCode = 666;
671  MakeClusterObsolete(icl);
672  } // icl
673 
674 
675  } // KillGarbageClusters
676 
677 
680  {
681  // Tries to merge overlapping clusters schematically shown below. The minimal condition is that both
682  // clusters have a length of at least minLen wires with minOvrLap of the minLen wires in the overlap region
683  // End Begin
684  // icl ------
685  // jcl ------
686  // End Begin
687  // This can occur when tracking cosmic rays through delta ray showers.
688  // If successfull the hits in clusters icl and jcl are merged into one long cluster
689  // and a short cluster if there are sufficient remaining hits.
690  // This routine changes the "pass" variable to define cuts and should NOT be used inside any pass loops
691 
692  unsigned short icl, jcl;
693 
694  bool chkprt = (fDebugWire == 666);
695  if(chkprt) mf::LogVerbatim("CC")<<"Inside MergeOverlap using clCTP "<<clCTP;
696 
697  unsigned short minLen = 6;
698  unsigned short minOvrLap = 2;
699 
700  // use the loosest dTick cut which is probably the last one
701  float maxDTick = fTimeDelta[fTimeDelta.size() - 1];
702 
703  unsigned int overlapSize, ii, indx, bWire, eWire;
704  unsigned int iht, jht;
705  float dang, prtime, dTick;
706  for(icl = 0; icl < tcl.size(); ++icl) {
707  if(tcl[icl].ID < 0) continue;
708  if(tcl[icl].CTP != clCTP) continue;
709  prt = chkprt && fDebugPlane == (int)clCTP;
710  if(tcl[icl].BeginVtx >= 0) continue;
711  if(tcl[icl].tclhits.size() < minLen) continue;
712  for(jcl = 0; jcl < tcl.size(); ++jcl) {
713  if(icl == jcl) continue;
714  if(tcl[jcl].ID < 0) continue;
715  if(tcl[jcl].CTP != clCTP) continue;
716  if(tcl[jcl].EndVtx >= 0) continue;
717  if(tcl[jcl].tclhits.size() < minLen) continue;
718  // icl Begin is not far enough DS from the end of jcl
719  if(tcl[icl].BeginWir < tcl[jcl].EndWir + minOvrLap) continue;
720  // and it doesn't end within the wire boundaries of jcl
721  if(tcl[icl].BeginWir > tcl[jcl].BeginWir - minOvrLap) continue;
722  // jcl End isn't far enough US from the end of icl
723  if(tcl[jcl].EndWir < tcl[icl].EndWir + minOvrLap) continue;
724  dang = std::abs(tcl[icl].BeginAng - tcl[jcl].EndAng);
725  if(prt) mf::LogVerbatim("CC")<<"MergeOverlap icl ID "<<tcl[icl].ID<<" jcl ID "<<tcl[jcl].ID<<" dang "<<dang;
726  if(dang > 0.5) continue;
727  overlapSize = tcl[icl].BeginWir - tcl[jcl].EndWir + 1;
728  eWire = tcl[jcl].EndWir;
729  bWire = tcl[icl].BeginWir;
730  if(prt) mf::LogVerbatim("CC")<<" Candidate icl ID "<<tcl[icl].ID<<" "<<tcl[icl].EndWir<<"-"<<tcl[icl].BeginWir<<" jcl ID "<<tcl[jcl].ID<<" "<<tcl[jcl].EndWir<<"-"<<tcl[jcl].BeginWir<<" overlapSize "<<overlapSize<<" bWire "<<bWire<<" eWire "<<eWire;
731  iht = 0; jht = 0;
732  for(ii = 0; ii < tcl[icl].tclhits.size(); ++ii) {
733  iht = tcl[icl].tclhits[ii];
734  if(fHits[iht].WireID().Wire < eWire) break;
735  } // ii
736  // require that the ends be similar in time
737  dTick = std::abs(fHits[iht].PeakTime() - tcl[jcl].EndTim);
738  if(dTick > maxDTick) continue;
739  if(prt) mf::LogVerbatim("CC")<<" dTick icl iht time "<<PrintHit(iht)<<" jcl EndTim "<<tcl[jcl].EndTim<<" dTick "<<dTick;
740  for(ii = 0; ii < tcl[jcl].tclhits.size(); ++ii) {
741  jht = tcl[jcl].tclhits[tcl[jcl].tclhits.size() - ii - 1];
742  if(fHits[jht].WireID().Wire > bWire) break;
743  } // ii
744  dTick = std::abs(fHits[jht].PeakTime() - tcl[icl].BeginTim);
745  if(dTick > maxDTick) continue;
746  if(prt) mf::LogVerbatim("CC")<<" dTick jcl jht time "<<PrintHit(jht)<<" icl BeginTim "<<tcl[icl].BeginTim<<" dTick "<<dTick;
747  // Calculate the line between iht and jht
748  clpar[0] = fHits[iht].PeakTime();
749  clpar[2] = fHits[iht].WireID().Wire;
750  clpar[1] = (fHits[jht].PeakTime() - fHits[iht].PeakTime()) / ((float)fHits[jht].WireID().Wire - clpar[2]);
751  // put the hits in the overlap region into a vector if they are close to the line
752  std::vector<unsigned int> oWireHits(overlapSize, INT_MAX);
753  std::vector<float> delta(overlapSize, maxDTick);
754  for(ii = 0; ii < tcl[icl].tclhits.size(); ++ii) {
755  iht = tcl[icl].tclhits[ii];
756  if(fHits[iht].WireID().Wire < eWire) break;
757  prtime = clpar[0] + clpar[1] * ((float)fHits[iht].WireID().Wire - clpar[2]);
758  dTick = std::abs(fHits[iht].PeakTime() - prtime);
759  indx = fHits[iht].WireID().Wire - eWire;
760  if(dTick > delta[indx]) continue;
761  delta[indx] = dTick;
762  oWireHits[indx] = iht;
763  } // ii
764  // enter the second set of hits
765  for(ii = 0; ii < tcl[jcl].tclhits.size(); ++ii) {
766  jht = tcl[jcl].tclhits[tcl[jcl].tclhits.size() - ii - 1];
767  if(fHits[jht].WireID().Wire > bWire) break;
768  prtime = clpar[0] + clpar[1] * ((float)fHits[jht].WireID().Wire - clpar[2]);
769  dTick = std::abs(fHits[jht].PeakTime() - prtime);
770  indx = fHits[jht].WireID().Wire - eWire;
771  if(dTick > delta[indx]) continue;
772  delta[indx] = dTick;
773  oWireHits[indx] = jht;
774  } // ii
775  // stuff them into fcl2hits
776  fcl2hits.clear();
777  for(ii = 0; ii < oWireHits.size(); ++ii) {
778  if(oWireHits[ii] == INT_MAX) continue;
779  iht = oWireHits[ii];
780  fcl2hits.push_back(iht);
781  if(prt) mf::LogVerbatim("CC")<<"hit "<<PrintHit(iht);
782  } // ii
783  if(fcl2hits.size() < 0.5 * overlapSize) continue;
784  if(fcl2hits.size() < 3) continue;
785  std::sort(fcl2hits.begin(), fcl2hits.end(), SortByLowHit);
786  FitCluster();
787  if(prt) mf::LogVerbatim("CC")<<" Overlap size "<<overlapSize<<" fit chisq "<<clChisq<<" nhits "<<fcl2hits.size();
788  if(clChisq > 20) continue;
789  // save these hits so we can paste them back on fcl2hits when merging
790  std::vector<unsigned int> oHits = fcl2hits;
791  // prepare to make a new cluster
792  TmpGet(jcl);
793  // resize it
794  unsigned short jclNewSize;
795  for(jclNewSize = 0; jclNewSize < fcl2hits.size(); ++jclNewSize) {
796  iht = fcl2hits[jclNewSize];
797  if(fHits[iht].WireID().Wire <= bWire) break;
798  } // jclNewSize
799  if(prt) {
800  mf::LogVerbatim("CC")<<"jcl old size "<<fcl2hits.size()<<" newSize "<<jclNewSize;
801  iht = fcl2hits[fcl2hits.size()-1];
802  unsigned int iiht = fcl2hits[jclNewSize-1];
803  mf::LogVerbatim("CC")<<"jcl old last wire "<<fHits[iht].WireID().Wire<<" After resize last wire "<<fHits[iiht].WireID().Wire;
804  }
805  fcl2hits.resize(jclNewSize);
806  // append the hits in the overlap region
807  fcl2hits.insert(fcl2hits.end(), oHits.begin(), oHits.end());
808  // now paste in the icl hits that are US of the overlap region
809  for(ii = 0; ii < tcl[icl].tclhits.size(); ++ii) {
810  iht = tcl[icl].tclhits[ii];
811  if((unsigned int)fHits[iht].WireID().Wire >= eWire) continue;
812  fcl2hits.insert(fcl2hits.end(),tcl[icl].tclhits.begin()+ii, tcl[icl].tclhits.end());
813  break;
814  }
815  clBeginSlp = tcl[jcl].BeginSlp;
816  clBeginSlpErr = tcl[jcl].BeginSlpErr;
817  clBeginAng = tcl[jcl].BeginAng;
818  clBeginWir = tcl[jcl].BeginWir;
819  clBeginTim = tcl[jcl].BeginTim;
820  clBeginChg = tcl[jcl].BeginChg;
821  clBeginChgNear = tcl[jcl].BeginChgNear;
822  // End info from icl
823  clEndSlp = tcl[icl].EndSlp;
824  clEndSlpErr = tcl[icl].EndSlpErr;
825  clEndAng = tcl[icl].EndAng;
826  clEndWir = tcl[icl].EndWir;
827  clEndTim = tcl[icl].EndTim;
828  clEndChg = tcl[icl].EndChg;
829  clEndChgNear = tcl[icl].EndChgNear;
830  clStopCode = tcl[icl].StopCode;
831  clProcCode = tcl[icl].ProcCode + 500;
832  MakeClusterObsolete(icl);
833  MakeClusterObsolete(jcl);
834  if(!TmpStore()) {
835  // Merged cluster is fubar. Try to recover
836  RestoreObsoleteCluster(icl);
837  RestoreObsoleteCluster(jcl);
838  CheckHitClusterAssociations();
839  continue;
840  }
841  CheckHitClusterAssociations();
842  tcl[tcl.size() - 1].BeginVtx = tcl[jcl].BeginVtx;
843  tcl[tcl.size() - 1].EndVtx = tcl[icl].BeginVtx;
844  // after this point any failure should result in a jcl loop break
845  if(prt) mf::LogVerbatim("CC")<<"MergeOverlap new long cluster ID "<<tcl[tcl.size()-1].ID<<" in clCTP "<<clCTP;
846  // icl cluster made obsolete so we must have done something
847  if(tcl[icl].ID < 0) break;
848  } // jcl
849  } // icl
850 
851  } // MergeOverlap()
852 
854  void ClusterCrawlerAlg::MakeClusterObsolete(unsigned short icl) {
855  short& ID = tcl[icl].ID;
856  if (ID <= 0) {
857  mf::LogError("CC")<<"Trying to make already-obsolete cluster obsolete ID = "<<ID;
858  return; // already obsolete
859  }
860  ID = -ID; // mark the cluster as obsolete
861 
862  // release the hits
863  for(unsigned int iht = 0; iht < tcl[icl].tclhits.size(); ++iht) inClus[tcl[icl].tclhits[iht]] = 0;
864 
865  } // ClusterCrawlerAlg::MakeClusterObsolete()
866 
868  void ClusterCrawlerAlg::RestoreObsoleteCluster(unsigned short icl) {
869  short& ID = tcl[icl].ID;
870  if(ID > 0) {
871  mf::LogError("CC")<<"Trying to restore non-obsolete cluster ID = "<<ID;
872  return;
873  }
874  ID = -ID;
875 
876  for(unsigned short iht = 0; iht < tcl[icl].tclhits.size(); ++iht) inClus[tcl[icl].tclhits[iht]] = ID;
877 
878  } // RestoreObsoleteCluster()
879 
880 
882  void ClusterCrawlerAlg::FclTrimUS(unsigned short nTrim)
883  {
884 
885  // Trims nTrim hits off the UpStream end of the fcl2hits, etc vectors.
886  if(nTrim == 0) return;
887 
888  if(nTrim >= fcl2hits.size()) nTrim = fcl2hits.size();
889 
890 // RestoreUnMergedClusterHits((short)nTrim);
891  for(unsigned short ii = 0; ii < nTrim; ++ii) {
892  fcl2hits.pop_back();
893  chifits.pop_back();
894  hitNear.pop_back();
895  chgNear.pop_back();
896  } // ii
897 
898  } // FclTrim
899 /*
901  void ClusterCrawlerAlg::ClearUnMergedHits()
902  {
903  // Erase all saved un-merged hits in unMergedHits. This is
904  // called after a tcl cluster is created from fcl2hits
905 
906  if(unMergedHits.size() == 0) return;
907 
908  for(auto fclIndex : fcl2hits) {
909  recob::Hit aHit = fHits[fclIndex];
910  if(aHit.GoodnessOfFit() != 6666) continue;
911  raw::TDCtick_t sTick = aHit.StartTick();
912  unsigned int firstHit;
913  for(firstHit = 0; firstHit < unMergedHits.size(); ++firstHit)
914  if(unMergedHits[firstHit].StartTick() == sTick) break;
915  if(firstHit > unMergedHits.size()-1) {
916  mf::LogError("CC")<<"ClearUnMergedHits coding error: firsthit "<<firstHit<<" > "<<unMergedHits.size();
917  return;
918  }
919  unsigned short oldMult = unMergedHits[firstHit].Multiplicity();
920  if(firstHit+oldMult > unMergedHits.size()) {
921  mf::LogError("CC")<<"ClearUnMergedHits coding error: firsthit "<<firstHit<<" + oldMult "<<oldMult<<" > "<<unMergedHits.size();
922  return;
923  }
924  unMergedHits.erase(unMergedHits.begin()+firstHit, unMergedHits.begin()+firstHit+oldMult);
925  } // fclIndex
926 
927  } // ClearUnMergedHits
928 
930  void ClusterCrawlerAlg::RestoreUnMergedHit(unsigned int theHit)
931  {
932  // Restores a single merged hit with the un-merged hits
933  if(theHit > fHits.size()-1) return;
934 
935  if(fHits[theHit].GoodnessOfFit() != 6666) return;
936 
937  if(unMergedHits.size() == 0) {
938  mf::LogError("CC")<<"RestoreUnMergedHit: No unMergedHits "
939  <<fHits[theHit].WireID().Plane<<":"<<fHits[theHit].WireID().Wire<<":"<<(int)fHits[theHit].PeakTime();
940  return;
941  }
942 
943  unsigned int thePlane = fHits[theHit].WireID().Plane;
944  unsigned int theWire = fHits[theHit].WireID().Wire;
945 
946  // bool tmp = (thePlane == 0);
947 // if(tmp) mf::LogVerbatim("CC")<<"RestoreUnMergedHit: restoring "<<thePlane<<":"<<theWire<<":"<<(int)fHits[theHit].PeakTime();
948 
949  if(thePlane != plane) {
950  mf::LogError("CC")<<"RestoreUnMergedHit: Mis-match between plane and hit WireID plane";
951  return;
952  }
953  if(WireHitRange[theWire].first < 0) {
954  mf::LogError("CC")<<"RestoreUnMergedHit: Requested hit is on a dead or non-existent wire";
955  return;
956  }
957 
958  raw::TDCtick_t sTick = fHits[theHit].StartTick();
959  // Find the starting index of the hit multiplet in unMergedHits
960  unsigned int firstHit;
961  for(firstHit = 0; firstHit < unMergedHits.size(); ++firstHit) {
962  if(unMergedHits[firstHit].WireID().Wire != fHits[theHit].WireID().Wire) continue;
963  if(unMergedHits[firstHit].StartTick() == sTick) break;
964  }
965 
966  // Find the starting index of the hit in fHits. Use WireHitRange to speed the search
967  int fHitsStart;
968  for(fHitsStart = WireHitRange[theWire].first; fHitsStart < WireHitRange[theWire].second; ++fHitsStart)
969  if(fHits[fHitsStart].StartTick() == sTick) break;
970  unsigned short oldMult = unMergedHits[firstHit].Multiplicity();
971  // restore all of the hits in the multiplet
972 // if(tmp) std::cout<<" Found in unMergedHits at "<<firstHit<<" FHits at "<<fHitsStart<<" oldMult "<<oldMult<<"\n";
973  for(unsigned short kk = 0; kk < oldMult; ++kk) {
974  if(fHits[fHitsStart + kk].StartTick() != unMergedHits[firstHit + kk].StartTick()) {
975  mf::LogError("CC")<<"RestoreUnMergedHit: Bad restore";
976  return;
977  }
978  fHits[fHitsStart + kk] = unMergedHits[firstHit + kk];
979  inClus[fHitsStart + kk] = 0;
980  }
981  unMergedHits.erase(unMergedHits.begin()+firstHit, unMergedHits.begin()+firstHit+oldMult);
982 
983  } // RestoreUnMergedHit
984 
986  void ClusterCrawlerAlg::RestoreUnMergedClusterHits(short ntrim)
987  {
988 
989  if(unMergedHits.size() == 0) return;
990  if(ntrim == 0) return;
991 
992  if(ntrim < 0) {
993  for(auto hiter = fcl2hits.begin(); hiter < fcl2hits.end(); ++hiter) RestoreUnMergedHit(*hiter);
994  } else {
995  short ntr = 0;
996  for(auto hiter = fcl2hits.crbegin(); hiter < fcl2hits.crend(); ++hiter) {
997  RestoreUnMergedHit(*hiter);
998  ++ntr;
999  if(ntr == ntrim) break;
1000  } // hiter
1001  }
1002 
1003  } // RestoreUnMergedClusterHits
1004 */
1006  void ClusterCrawlerAlg::CheckHitClusterAssociations()
1007  {
1008  // check hit - cluster associations
1009 
1010  if(fHits.size() != inClus.size()) {
1011  mf::LogError("CC")<<"CHCA: Sizes wrong "<<fHits.size()<<" "<<inClus.size();
1012  return;
1013  }
1014 
1015  unsigned int iht, nErr = 0;
1016  short clID;
1017 
1018  // check cluster -> hit association
1019  for(unsigned short icl = 0; icl < tcl.size(); ++icl) {
1020  if(tcl[icl].ID < 0) continue;
1021  clID = tcl[icl].ID;
1022  for(unsigned short ii = 0; ii < tcl[icl].tclhits.size(); ++ii) {
1023  iht = tcl[icl].tclhits[ii];
1024  if(iht > fHits.size() - 1) {
1025  mf::LogError("CC")<<"CHCA: Bad tclhits index "<<iht<<" fHits size "<<fHits.size();
1026  return;
1027  } // iht > fHits.size() - 1
1028  if(inClus[iht] != clID) {
1029  mf::LogError("CC")<<"CHCA: Bad cluster -> hit association. clID "<<clID<<" hit inClus "<<inClus[iht]<<" ProcCode "<<tcl[icl].ProcCode<<" CTP "<<tcl[icl].CTP;
1030  ++nErr;
1031  if(nErr > 10) return;
1032  }
1033  } // ii
1034  } // icl
1035 
1036  // check hit -> cluster association
1037  unsigned short icl;
1038  for(iht = 0; iht < fHits.size(); ++iht) {
1039  if(inClus[iht] <= 0) continue;
1040  icl = inClus[iht] - 1;
1041  // see if the cluster is obsolete
1042  if(tcl[icl].ID < 0) {
1043  mf::LogError("CC")<<"CHCA: Hit associated with an obsolete cluster. hit W:T "<<fHits[iht].WireID().Wire<<":"<<(int)fHits[iht].PeakTime()
1044  <<" tcl[icl].ID "<<tcl[icl].ID;
1045  ++nErr;
1046  if(nErr > 10) return;
1047  }
1048  if (std::find(tcl[icl].tclhits.begin(), tcl[icl].tclhits.end(), iht) == tcl[icl].tclhits.end()) {
1049  mf::LogError("CC")<<"CHCA: Bad hit -> cluster association. hit index "<<iht
1050  <<" W:T "<<fHits[iht].WireID().Wire<<":"<<(int)fHits[iht].PeakTime()<<" inClus "<<inClus[iht];
1051  ++nErr;
1052  if(nErr > 10) return;
1053  }
1054  } // iht
1055 
1056  } // CheckHitClusterAssociations()
1057 
1059  void ClusterCrawlerAlg::RemoveObsoleteHits() {
1060 
1061  unsigned int destHit = 0;
1062 
1063  if(fHits.size() != inClus.size()) {
1064  mf::LogError("CC")<<"RemoveObsoleteHits size mis-match "<<fHits.size()<<" "<<inClus.size();
1065  return;
1066  }
1067 
1068  unsigned short icl;
1069  for(unsigned int srcHit = 0; srcHit < fHits.size(); ++srcHit) {
1070  if(inClus[srcHit] < 0) continue;
1071  if(srcHit != destHit) {
1072  fHits[destHit] = std::move(fHits[srcHit]);
1073  inClus[destHit] = inClus[srcHit];
1074  if(inClus[destHit] > 0) {
1075  // hit is in a cluster. Find it and change the index
1076  icl = inClus[destHit] - 1;
1077  auto& hits = tcl[icl].tclhits;
1078  auto iHitIndex = std::find(hits.begin(), hits.end(), srcHit);
1079  if (iHitIndex == hits.end()) {
1080  mf::LogError("CC")<< "RemoveObsoleteHits: Hit #" << srcHit << " not found in cluster ID "<< inClus[destHit];
1081  } else {
1082  *iHitIndex = destHit; // update the index
1083  }
1084  } // inClus[destHit] > 0
1085  }
1086  ++destHit;
1087  } // srcHit
1088 
1089  fHits.resize(destHit);
1090  inClus.resize(destHit);
1091 
1092  } // RemoveObsoleteHits()
1093 
1095  void ClusterCrawlerAlg::ChkClusterDS() {
1096  // Try to extend clusters DS by a few wires.
1097  // DS hits may not have been included in a cluster if they have high
1098  // multiplicity or high charge.
1099  // Ref ClusterLoop cuts for starting a seed cluster.
1100 
1101  prt = (fDebugPlane == 3);
1102 
1103  // use the most generous kink angle cut
1104  float dThCut = fKinkAngCut[fNumPass - 1];
1105 
1106  if(prt) mf::LogVerbatim("CC")<<"ChkClusterDS clCTP "<<clCTP<<" kink angle cut "<<dThCut;
1107 
1108  const unsigned short tclsize = tcl.size();
1109  bool didMerge, skipit;
1110  unsigned short icl, ii, nhm, iv;
1111  unsigned int iht;
1112 
1113  // first merge any hits on the DS end of clusters
1114  for(icl = 0; icl < tclsize; ++icl) {
1115  if(tcl[icl].ID < 0) continue;
1116  if(tcl[icl].CTP != clCTP) continue;
1117  // ignore clusters that have a Begin vertex
1118  if(tcl[icl].BeginVtx >= 0) continue;
1119  // and clusters near a vertex
1120  skipit = false;
1121  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
1122  if(vtx[iv].CTP != clCTP) continue;
1123  if(std::abs(vtx[iv].Wire - tcl[icl].BeginWir) > 4) continue;
1124  if(std::abs(vtx[iv].Time - tcl[icl].BeginTim) > 20) continue;
1125  skipit = true;
1126  break;
1127  }
1128  if(skipit) continue;
1129  // check the first few hits
1130  nhm = 0;
1131  for(ii = 0; ii < 3; ++ii) {
1132  iht = fcl2hits[ii];
1133  if(fHits[iht].Multiplicity() > 1) {
1134  MergeHits(iht, didMerge);
1135  if(didMerge) ++nhm;
1136  }
1137  } // ii
1138  if(nhm > 0) {
1139  // update the Begin parameters in-place
1140  FitClusterMid(icl, 0, 3);
1141  tcl[icl].BeginTim = clpar[0];
1142  tcl[icl].BeginSlp = clpar[1];
1143  tcl[icl].BeginAng = atan(fScaleF * clpar[1]);
1144  tcl[icl].BeginSlpErr = clparerr[1];
1145  tcl[icl].BeginChg = fAveChg;
1146  tcl[icl].ProcCode += 5000;
1147  if(prt) mf::LogVerbatim("CC")<<"ChkClusterDS: Merge hits on cluster "<<tcl[icl].ID;
1148  } // nhm > 0
1149  } // icl
1150 
1151  float thhits, prevth, hitrms, rmsrat;
1152  bool ratOK;
1153  std::vector<unsigned int> dshits;
1154  for(icl = 0; icl < tclsize; ++icl) {
1155  if(tcl[icl].ID < 0) continue;
1156  if(tcl[icl].CTP != clCTP) continue;
1157  // ignore clusters that have a Begin vertex
1158  if(tcl[icl].BeginVtx >= 0) continue;
1159  // and clusters near a vertex
1160  skipit = false;
1161  for(iv = 0; iv < vtx.size(); ++iv) {
1162  if(vtx[iv].CTP != clCTP) continue;
1163  if(std::abs(vtx[iv].Wire - tcl[icl].BeginWir) > 4) continue;
1164  if(std::abs(vtx[iv].Time - tcl[icl].BeginTim) > 20) continue;
1165  skipit = true;
1166  break;
1167  }
1168  if(skipit) continue;
1169  // ignore clusters with lots of nearby charge
1170 // if(tcl[icl].BeginChgNear > fChgNearCut) continue;
1171  // find the angle using the first 2 hits
1172  unsigned int ih0 = tcl[icl].tclhits[1];
1173  unsigned int ih1 = tcl[icl].tclhits[0];
1174  const float slp = (fHits[ih1].PeakTime() - fHits[ih0].PeakTime()) /
1175  (fHits[ih1].WireID().Wire - fHits[ih0].WireID().Wire);
1176  prevth = std::atan(fScaleF * slp);
1177  // move the "origin" to the first hit
1178  ih0 = ih1;
1179  unsigned int wire = fHits[ih0].WireID().Wire;
1180  hitrms = fHits[ih0].RMS();
1181  float time0 = fHits[ih0].PeakTime();
1182  float prtime;
1183  dshits.clear();
1184  // follow DS a few wires. Stop if any encountered
1185  // hit is associated with a cluster
1186  for(ii = 0; ii < 4; ++ii) {
1187  ++wire;
1188  if(wire > fLastWire) break;
1189  prtime = time0 + slp;
1190  if(prt) mf::LogVerbatim("CC")<<"ChkClusterDS: Try to extend "
1191  <<tcl[icl].ID<<" to W:T "<<wire<<" hitrms "<<hitrms<<" prevth "<<prevth<<" prtime "<<(int)prtime;
1192  // stop if no hits on this wire
1193  if(WireHitRange[wire].first == -2) break;
1194  unsigned int firsthit = WireHitRange[wire].first;
1195  unsigned int lasthit = WireHitRange[wire].second;
1196  bool hitAdded = false;
1197  for(ih1 = firsthit; ih1 < lasthit; ++ih1) {
1198  if(inClus[ih1] != 0) continue;
1199  if(prtime < fHits[ih1].PeakTimeMinusRMS(5)) continue;
1200  if(prtime > fHits[ih1].PeakTimePlusRMS(5)) continue;
1201  const float slp = (fHits[ih1].PeakTime() - fHits[ih0].PeakTime()) /
1202  (fHits[ih1].WireID().Wire - fHits[ih0].WireID().Wire);
1203  thhits = std::atan(fScaleF * slp);
1204  if(prt) mf::LogVerbatim("CC")<<" theta "<<thhits<<" prevth "<<prevth<<" cut "<<dThCut;
1205  if(std::abs(thhits - prevth) > dThCut) continue;
1206  // make a hit rms cut for small angle clusters
1207  ratOK = true;
1208  if(std::abs(slp) < fLAClusSlopeCut) {
1209  rmsrat = fHits[ih1].RMS() / hitrms;
1210  ratOK = rmsrat > 0.3 && rmsrat < 3;
1211  } else {
1212  // merge the hits
1213  bool didMerge;
1214  MergeHits(ih1, didMerge);
1215  }
1216  if(prt) mf::LogVerbatim("CC")<<" rmsrat "<<rmsrat<<" OK? "<<ratOK;
1217  // require small angle and not wildly different width compared
1218  // to the first hit in the cluster
1219  // TODO handle hit multiplets here
1220  if(ratOK) {
1221  dshits.push_back(ih1);
1222  hitAdded = true;
1223  prevth = thhits;
1224  ih0 = ih1;
1225  if(prt) mf::LogVerbatim("CC")<<" Add hit "<<fHits[ih1].WireID().Wire
1226  <<":"<<(int)fHits[ih1].PeakTime()<<" rmsrat "<<rmsrat;
1227  break;
1228  }
1229  } // ih1
1230  // stop looking if no hit was added on this wire
1231  if(!hitAdded) break;
1232  } // ii
1233  // Found hits not associated with a different cluster
1234  if(dshits.size() > 0) {
1235  // put the tcl cluster into the working vectors
1236  TmpGet(icl);
1237  // clobber the hits
1238  fcl2hits.clear();
1239  // sort the DS hits
1240  if(dshits.size() > 1) std::sort(dshits.begin(), dshits.end(), SortByLowHit);
1241  // stuff them into fcl2hits
1242  fcl2hits = dshits;
1243  // Append the existing hits
1244  for(ii = 0; ii < tcl[icl].tclhits.size(); ++ii) {
1245  // un-assign the hits so that TmpStore will re-assign them
1246  iht = tcl[icl].tclhits[ii];
1247  inClus[iht] = 0;
1248  fcl2hits.push_back(iht);
1249  }
1250  clProcCode += 5000;
1251  pass = fNumPass - 1;
1252  FitClusterChg();
1253  clBeginChg = fAveChg;
1254  // declare the old one obsolete
1255  MakeClusterObsolete(icl);
1256  // add the new one
1257  if(!TmpStore()) {
1258  mf::LogError("CC")<<"ChkClusterDS TmpStore failed while extending cluster ID "<<tcl[icl].ID;
1259  continue;
1260  }
1261  const size_t newcl = tcl.size() -1;
1262  if(prt) { mf::LogVerbatim("CC")<<" Store "<<newcl; }
1263  tcl[newcl].BeginVtx = tcl[icl].BeginVtx;
1264  tcl[newcl].EndVtx = tcl[icl].EndVtx;
1265  } // dshits.size() > 0
1266  } // icl
1267  } // ChkClusterDS
1268 
1269 
1271  bool ClusterCrawlerAlg::CrawlVtxChk2()
1272  {
1273  // returns true if the (presumably short) cluster under construction
1274  // resides between a vertex and another cluster that is associated with
1275  // that vertex
1276 
1277  if(vtx.size() == 0) return false;
1278  if(fcl2hits.size() == 0) return false;
1279 
1280  unsigned int iht = fcl2hits.size() - 1;
1281  unsigned short icl;
1282  float wire0 = (0.5 + fHits[fcl2hits[iht]].WireID().Wire);
1283  float dt;
1284  float thclus = std::atan(fScaleF * clpar[1]);
1285 
1286  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
1287  if(vtx[iv].CTP != clCTP) continue;
1288  if(wire0 < vtx[iv].Wire) continue;
1289  if(wire0 > vtx[iv].Wire + 10) continue;
1290  dt = clpar[0] + (vtx[iv].Wire - wire0) * clpar[1] - vtx[iv].Time;
1291  if(std::abs(dt) > 10) continue;
1292  // cluster points to an US vertex. See if the angle is similar to
1293  // cluster associated with this vertex
1294  for(icl = 0; icl < tcl.size(); ++icl) {
1295  if(tcl[icl].CTP != clCTP) continue;
1296  if(tcl[icl].EndVtx != iv) continue;
1297  if(std::abs(tcl[icl].EndAng - thclus) < fKinkAngCut[pass]) return true;
1298  }
1299  }
1300 
1301  return false;
1302 
1303  } // CrawlVtxChk2()
1304 
1306  bool ClusterCrawlerAlg::CrawlVtxChk(unsigned int kwire)
1307  {
1308 
1309  // returns true if the cluster is near a vertex on wire kwire
1310  if(vtx.size() == 0) return false;
1311  unsigned int iht = fcl2hits.size() - 1;
1312  float wire0 = (0.5 + fHits[fcl2hits[iht]].WireID().Wire);
1313  float prtime = clpar[0] + (kwire - wire0) * clpar[1];
1314  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
1315  if(vtx[iv].CTP != clCTP) continue;
1316  if((unsigned int)(0.5 + vtx[iv].Wire) != kwire) continue;
1317  if(std::abs(prtime - vtx[iv].Time) < 10) return true;
1318  }
1319  return false;
1320  } // CrawlVtxChk()
1322  void ClusterCrawlerAlg::VtxConstraint(unsigned int iwire, unsigned int ihit, unsigned int jwire, unsigned int& useHit, bool& doConstrain)
1323  {
1324  // checks hits on wire jwire to see if one is on a line between a US vertex
1325  // and the hit ihit on wire iwire. If one is found, doConstrain is set true
1326  // and the hit index is returned.
1327  doConstrain = false;
1328  if(vtx.size() == 0) return;
1329  // no vertices made yet on the first pass
1330  if(pass == 0) return;
1331  // skip if vertices were not requested to be made on the previous pass
1332  if( !fFindVertices[pass - 1] ) return;
1333 
1334  if(jwire > WireHitRange.size() - 1) {
1335  mf::LogError("CC")<<"VtxConstraint fed bad jwire "<<jwire<<" WireHitRange size "<<WireHitRange.size();
1336  return;
1337  }
1338 
1339  unsigned int jfirsthit = WireHitRange[jwire].first;
1340  unsigned int jlasthit = WireHitRange[jwire].second;
1341  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
1342  if(vtx[iv].CTP != clCTP) continue;
1343  // vertex must be US of the cluster
1344  if(vtx[iv].Wire > jwire) continue;
1345  // but not too far US
1346  if(vtx[iv].Wire < jwire - 10) continue;
1347  recob::Hit const& hit = fHits[ihit];
1348  clpar[0] = hit.PeakTime();
1349  clpar[1] = (vtx[iv].Time - hit.PeakTime()) / (vtx[iv].Wire - iwire);
1350  clpar[2] = hit.WireID().Wire;
1351  float prtime = clpar[0] + clpar[1] * (jwire - iwire);
1352  for(unsigned int jhit = jfirsthit; jhit < jlasthit; ++jhit) {
1353  if(inClus[jhit] != 0) continue;
1354  const float tdiff = std::abs(fHits[jhit].TimeDistanceAsRMS(prtime));
1355  if(tdiff < 2.5) {
1356  useHit = jhit;
1357  doConstrain = true;
1358  return;
1359  }
1360  } // jhit
1361  } // iv
1362  } // VtxConstraint()
1363 
1365 
1366  void ClusterCrawlerAlg::RefineVertexClusters(unsigned short iv)
1367  {
1368 
1369  // Try to attach or remove hits on the ends of vertex clusters
1370 
1371  std::vector<unsigned short> begClusters;
1372  std::vector<short> begdW;
1373  std::vector<unsigned short> endClusters;
1374  std::vector<short> enddW;
1375 
1376  unsigned int vWire = (unsigned int)(vtx[iv].Wire + 0.5);
1377  unsigned int vWireErr = (unsigned int)(2 * vtx[iv].WireErr);
1378  unsigned int vWireLo = vWire - vWireErr;
1379  unsigned int vWireHi = vWire + vWireErr;
1380 
1381  unsigned short icl, ii;
1382  short dW;
1383  bool needsWork = false;
1384  short maxdW = -100;
1385  short mindW = 100;
1386  for(icl = 0; icl < tcl.size(); ++icl) {
1387  if(tcl[icl].ID < 0) continue;
1388  if(tcl[icl].CTP != vtx[iv].CTP) continue;
1389  if(tcl[icl].BeginVtx == iv) {
1390  dW = vWire - tcl[icl].BeginWir;
1391  if(dW > maxdW) maxdW = dW;
1392  if(dW < mindW) mindW = dW;
1393  if(std::abs(dW) > 1) needsWork = true;
1394  // TODO: Check dTime also?
1395  begClusters.push_back(icl);
1396  begdW.push_back(dW);
1397  }
1398  if(tcl[icl].EndVtx == iv) {
1399  dW = vWire - tcl[icl].EndWir;
1400  if(dW > maxdW) maxdW = dW;
1401  if(dW < mindW) mindW = dW;
1402  if(std::abs(dW) > 1) needsWork = true;
1403  endClusters.push_back(icl);
1404  enddW.push_back(dW);
1405  }
1406  } // icl
1407 
1408  if(vtxprt) mf::LogVerbatim("CC")<<"RefineVertexClusters: vertex "<<iv<<" needsWork "<<needsWork
1409  <<" mindW "<<mindW<<" maxdW "<<maxdW<<" vWireErr "<<vWireErr;
1410 
1411  if(!needsWork) return;
1412 
1413  // See if we can move the vertex within errors to reconcile the differences
1414  // without altering the clusters
1415  if( ((unsigned int)std::abs(mindW) < vWireErr) && ((unsigned int)std::abs(maxdW) < vWireErr) && std::abs(maxdW - mindW) < 2) {
1416  if(vtxprt) mf::LogVerbatim("CC")<<" Move vtx wire "<<vtx[iv].Wire;
1417  vtx[iv].Wire -= (float)(maxdW + mindW)/2;
1418  if(vtxprt) mf::LogVerbatim("CC")<<" to "<<vtx[iv].Wire;
1419  // TODO: Fix the vertex time here if necessary
1420  vtx[iv].Fixed = true;
1421  // try to attach other clusters
1422  VertexCluster(iv);
1423  return;
1424  }
1425 
1426  // Check the vertex End clusters
1427  unsigned short newSize;
1428  for(ii = 0; ii < endClusters.size(); ++ii) {
1429  icl = endClusters[ii];
1430  if(vtxprt) mf::LogVerbatim("CC")<<" endCluster "<<tcl[icl].ID<<" dW "<<enddW[ii]<<" vWire "<<vWire;
1431  if(tcl[icl].EndWir < vWire) {
1432  // vertex is DS of the cluster end -> remove hits
1433  TmpGet(icl);
1434  newSize = fcl2hits.size();
1435  for(auto hiter = fcl2hits.rbegin(); hiter < fcl2hits.rend(); ++hiter) {
1436  if(fHits[*hiter].WireID().Wire > vWire) break;
1437  --newSize;
1438  }
1439  // release the hits
1440  for(auto hiter = fcl2hits.begin(); hiter < fcl2hits.end(); ++hiter) inClus[*hiter] = 0;
1441  // shorten the cluster
1442  fcl2hits.resize(newSize);
1443  MakeClusterObsolete(icl);
1444  // fit
1445  FitCluster();
1446  clProcCode += 700;
1447  // store it
1448  TmpStore();
1449  tcl[tcl.size()-1].EndVtx = iv;
1450  // update the vertex association
1451  if(vtxprt) mf::LogVerbatim("CC")<<" modified cluster "<<tcl[icl].ID<<" -> "<<tcl[tcl.size()-1].ID;
1452  } // tcl[icl].EndWir < vWire
1453  else if(tcl[icl].EndWir > vWire) {
1454  mf::LogVerbatim("CC")<<"RefineVertexClusters: Write some EndVtx code";
1455  } //
1456  } // ii endClusters
1457 
1458  if(begClusters.size() > 0) mf::LogVerbatim("CC")<<"RefineVertexClusters: Write some BeginVtx code";
1459 
1460  if(mindW < 0 && maxdW > 0) {
1461  // vertex wire is in between the ends of the clusters
1462  // inspect the hits on both clusters near the vertex. The vertex should probably be on the hit
1463  // with the highest charge
1464  int vtxHit = -1;
1465  unsigned short clsBigChg = 0;
1466  float bigChg = 0;
1467  unsigned int iht;
1468  unsigned int ihit;
1469  // check the begClusters
1470  for(ii = 0; ii < begClusters.size(); ++ii) {
1471  icl = begClusters[ii];
1472  for(iht = 0; iht < tcl[icl].tclhits.size(); ++iht) {
1473  ihit = tcl[icl].tclhits[iht];
1474  if(fHits[ihit].Integral() > bigChg) {
1475  bigChg = fHits[ihit].Integral();
1476  vtxHit = ihit;
1477  clsBigChg = icl;
1478  }
1479  if(fHits[ihit].WireID().Wire < vWireLo) break;
1480  } // iht
1481  } // ii
1482  // now check the endClusters
1483  for(ii = 0; ii < endClusters.size(); ++ii) {
1484  icl = endClusters[ii];
1485  for(iht = 0; iht < tcl[icl].tclhits.size(); ++iht) {
1486  ihit = tcl[icl].tclhits[tcl[icl].tclhits.size() - 1 - iht];
1487  if(fHits[ihit].Integral() > bigChg) {
1488  bigChg = fHits[ihit].Integral();
1489  vtxHit = ihit;
1490  clsBigChg = icl;
1491  }
1492  if(fHits[ihit].WireID().Wire > vWireHi) break;
1493  } // iht
1494  } // ii
1495  if(vtxHit > 0) {
1496  if(vtxprt) mf::LogVerbatim("CC")<<" moving vertex location to hit "
1497  <<fHits[vtxHit].WireID().Wire<<":"<<(int)fHits[vtxHit].PeakTime()<<" on cluster "<<tcl[clsBigChg].ID;
1498  vtx[iv].Wire = fHits[vtxHit].WireID().Wire;
1499  vtx[iv].Time = fHits[vtxHit].PeakTime();
1500  vtx[iv].Fixed = true;
1501  } // vtxHit > 0
1502  } // mindW < 0 && maxdW > 0
1503 
1504  FitVtx(iv);
1505 
1506  } // RefineVertexClusters
1507 
1509  bool ClusterCrawlerAlg::VtxClusterSplit()
1510  {
1511 
1512  // split clusters that cross vertices
1513 
1514  vtxprt = (fDebugPlane >= 0 && fDebugWire == 5555);
1515 
1516  if(vtxprt) mf::LogVerbatim("CC")<<"VtxClusterSplit ";
1517 
1518  if(vtx.size() == 0) return false;
1519  unsigned short tclsize = tcl.size();
1520  if(tclsize < 2) return false;
1521 
1522 // float dth;
1523  bool didit;
1524  bool didSomething = false;
1525 
1526  for(unsigned short icl = 0; icl < tclsize; ++icl) {
1527  if(tcl[icl].ID < 0) continue;
1528  if(tcl[icl].CTP != clCTP) continue;
1529  // ignore short clusters
1530  if(tcl[icl].tclhits.size() < 5) continue;
1531  // check vertices
1532  didit = false;
1533  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
1534  if(vtx[ivx].CTP != clCTP) continue;
1535  // abandoned vertex?
1536  if(vtx[ivx].NClusters == 0) continue;
1537  // already assigned to this vertex?
1538  if(tcl[icl].BeginVtx == ivx) continue;
1539  if(tcl[icl].EndVtx == ivx) continue;
1540 /*
1541  // long dead-straight cluster?
1542  if(tcl[icl].tclhits.size() > 100) {
1543  dth = tcl[icl].BeginAng - tcl[icl].EndAng;
1544  if(fabs(dth) < 0.1) continue;
1545  }
1546 */
1547  // vertex wire in-between the cluster ends?
1548  if(vtx[ivx].Wire < tcl[icl].EndWir) continue;
1549  if(vtx[ivx].Wire > tcl[icl].BeginWir) continue;
1550  // vertex time in-between the cluster ends?
1551  float hiTime = tcl[icl].BeginTim;
1552  if(tcl[icl].EndTim > hiTime) hiTime = tcl[icl].EndTim;
1553  if(vtx[ivx].Time > hiTime + 5) continue;
1554  float loTime = tcl[icl].BeginTim;
1555  if(tcl[icl].EndTim < loTime) loTime = tcl[icl].EndTim;
1556  if(vtx[ivx].Time < loTime - 5) continue;
1557  // find the hit on the cluster that is closest to the vertex on the
1558  // DS side
1559  if(vtxprt) mf::LogVerbatim("CC")<<" Chk cluster ID "<<tcl[icl].ID<<" with vertex "<<ivx;
1560  short ihvx = -99;
1561  // nSplit is the index of the hit in the cluster where we will
1562  // split it if all requirements are met
1563  unsigned short nSplit = 0;
1564  unsigned short nLop = 0;
1565  unsigned int iht;
1566  for(unsigned short ii = tcl[icl].tclhits.size()-1; ii > 0 ; --ii) {
1567  iht = tcl[icl].tclhits[ii];
1568  ++nLop;
1569  if(fHits[iht].WireID().Wire >= vtx[ivx].Wire) {
1570  nSplit = ii;
1571  if(vtxprt) mf::LogVerbatim("CC")<<"Split cluster "<<tcl[icl].ID<<" at wire "<<fHits[iht].WireID().Wire
1572  <<" nSplit "<<nSplit;
1573  ihvx = iht;
1574  break;
1575  }
1576  } // ii
1577  // found the wire. Now make a rough time cut
1578  if(ihvx < 0) continue;
1579 // short dtime = std::abs(short(fHits[ihvx].PeakTime() - vtx[ivx].Time));
1580 // if(vtxprt) mf::LogVerbatim("CC")<<" Delta time "<<dtime;
1581  if(fabs(fHits[ihvx].PeakTime() - vtx[ivx].Time) > 10) continue;
1582  // check the angle between the crossing cluster icl and the
1583  // clusters that comprise the vertex.
1584  // First decide which end of cluster icl to use to define the angle
1585  float iclAng = 0.;
1586  if(nSplit > tcl[icl].tclhits.size() / 2) {
1587  iclAng = tcl[icl].EndAng;
1588  } else {
1589  iclAng = tcl[icl].BeginAng;
1590  }
1591  if(vtxprt) mf::LogVerbatim("CC")<<" iclAng "<<iclAng;
1592  // check angle wrt the the vertex clusters
1593  bool angOK = false;
1594  for(unsigned short jcl = 0; jcl < tclsize; ++jcl) {
1595  if(tcl[jcl].ID < 0) continue;
1596  if(tcl[jcl].CTP != clCTP) continue;
1597  if(tcl[jcl].BeginVtx == ivx) {
1598  if(fabs(tcl[jcl].BeginAng - iclAng) > 0.4) {
1599  // large angle difference. Set the flag
1600  angOK = true;
1601  break;
1602  }
1603  } // tcl[jcl].BeginVtx == ivx
1604  if(tcl[jcl].EndVtx == ivx) {
1605  if(fabs(tcl[jcl].EndAng - iclAng) > 0.4) {
1606  // large angle difference. Set the flag
1607  angOK = true;
1608  break;
1609  }
1610  } // tcl[jcl].EndVtx == ivx
1611  } // jcl
1612  // time to split or chop
1613  if(angOK) {
1614  if(vtxprt) mf::LogVerbatim("CC")<<"Split/Chop at pos "<<nLop;
1615  if(nLop < 3) {
1616  // lop off hits at the US end
1617  // Put the cluster in the local arrays
1618  TmpGet(icl);
1619  for(unsigned short ii = 0; ii < nLop; ++ii) {
1620  unsigned int iht = fcl2hits[fcl2hits.size()-1];
1621  inClus[iht] = 0;
1622  fcl2hits.pop_back();
1623  }
1624  // store it
1625  clProcCode += 1000;
1626  // declare this cluster obsolete
1627  MakeClusterObsolete(icl);
1628  // store the new one
1629  if(!TmpStore()) continue;
1630  unsigned short newcl = tcl.size() - 1;
1631  tcl[newcl].BeginVtx = tcl[icl].BeginVtx;
1632  tcl[newcl].EndVtx = ivx;
1633  } else {
1634  // split the cluster into two
1635  // correct the split position
1636  ++nSplit;
1637  if(SplitCluster(icl, nSplit, ivx)) {
1638  tcl[tcl.size()-1].ProcCode += 1000;
1639  tcl[tcl.size()-2].ProcCode += 1000;
1640  }
1641  }
1642  didit = true;
1643  didSomething = true;
1644  } // angOK
1645  if(didit) break;
1646  } // ivx
1647  } // icl
1648 
1649  return didSomething;
1650 
1651  } // VtxClusterSplit()
1652 
1654  void ClusterCrawlerAlg::MergeHits(const unsigned int theHit, bool& didMerge) {
1655  // Merge all unused separate hits in the multiplet of which
1656  // theHit is a member into one hit (= theHit).
1657  // Mark the merged hits other than theHit obsolete.
1658  // Hits in the multiplet that are associated with an existing cluster are
1659  // not affected.
1660  // Hit multiplicity is reworked (including all the hits in the multiplet).
1661  // Used hits have the multiplicity and index corrected too; the local
1662  // index reflects the peak time.
1663  // Note that theHit may or may not be marked free (usually, it is not)
1664 
1665  didMerge = false;
1666 
1667  if(theHit > fHits.size() - 1) {
1668 // mf::LogError("CC")<<"Bad theHit";
1669  return;
1670  }
1671 
1672  recob::Hit const& hit = fHits[theHit];
1673 
1674  // don't bother trying to merge an already merged hit
1675  if(fHits[theHit].GoodnessOfFit() == 6666) {
1676  if(prt) mf::LogVerbatim("CC")<<"MergeHits Trying to merge already merged hit "
1677  <<hit.WireID().Plane<<":"<<hit.WireID().Wire<<":"<<(int)hit.PeakTime()
1678  <<" Multiplicity "<<hit.Multiplicity()<<" theHit "<<theHit;
1679  return;
1680  }
1681 
1682  // don't merge if it isn't available
1683  if(!mergeAvailable[theHit]) {
1684 // if(prt) mf::LogVerbatim("CC")<<"MergeHits "<<hit.WireID().Plane<<":"<<hit.WireID().Wire<<":"<<(int)hit.PeakTime()<<" Multiplicity "<<hit.Multiplicity()<<" theHit "<<theHit<<" is not available";
1685  return;
1686  }
1687 
1688  if(hit.Multiplicity() < 2) return;
1689 
1690 // if(prt) mf::LogVerbatim("CC")<<"MergeHits "<<hit.WireID().Plane<<":"<<hit.WireID().Wire<<":"<<(int)hit.PeakTime()<<" Multiplicity "<<hit.Multiplicity()<<" theHit "<<theHit;
1691 // if(prt && fcl2hits.size() > 0)mf::LogVerbatim("CC")<<" Seed hit "<<fHits[fcl2hits[0]].WireID().Wire<<":"<<(int)fHits[fcl2hits[0]].PeakTime();
1692 
1693  // number of hits in this hit multiplet
1694  std::pair<size_t, size_t> MultipletRange = FindHitMultiplet(theHit);
1695 
1696  // ensure that this is a high multiplicity hit:
1697  if (MultipletRange.second <= MultipletRange.first) return;
1698 
1699  // do a quick check to see how many hits are available to be merged
1700  unsigned short nAvailable = 0;
1701  unsigned short nInClus = 0;
1702  for(size_t jht = MultipletRange.first; jht < MultipletRange.second; ++jht) {
1703  if(jht == theHit) continue;
1704  if(fHits[jht].GoodnessOfFit() == 6666) continue;
1705  if(inClus[jht] != 0) {
1706  ++nInClus;
1707  continue;
1708  }
1709  ++nAvailable;
1710  } // jht
1711  if(nAvailable == 0) return;
1712  // don't merge if any hit is used
1713  if(nInClus > 0) return;
1714 
1715  // calculate the Charge normalization factor using the hit information
1716  // instead of passing CCHitFinder ChgNorms all the way down here
1717  float chgNorm = 2.507 * hit.PeakAmplitude() * hit.RMS() / hit.Integral();
1718 
1719  short loTime = 9999;
1720  short hiTime = 0;
1721  unsigned short nGaus = 1;
1722  float hitSep;
1723  // number of hits that are close to theHit
1724  unsigned short nclose = 0;
1725  // find the time range for the hit multiplet
1726  for(size_t jht = MultipletRange.first; jht < MultipletRange.second; ++jht) {
1727  if(inClus[jht] < 0) continue;
1728  recob::Hit const& other_hit = fHits[jht];
1729 /*
1730  if(prt) {
1731  mf::LogVerbatim("CC")
1732  <<" P:W:T "<<plane<<":"<<other_hit.WireID().Wire<<":"<<(int)other_hit.PeakTime()
1733  <<" Amp "<<(int)other_hit.PeakAmplitude()
1734  <<" RMS "<<other_hit.RMS()
1735  <<" Charge "<<(int)other_hit.Integral()
1736  <<" inClus "<<inClus[jht];
1737  }
1738 */
1739  // error checking
1740  if((other_hit.StartTick() != hit.StartTick())
1741  || (other_hit.WireID() != hit.WireID()))
1742  {
1743 /*
1744  mf::LogError("CC")<<"Hit multiplet ID error "
1745  << other_hit.WireID() << " @" << other_hit.StartTick()
1746  << " " << other_hit.LocalIndex() << "/" << other_hit.Multiplicity()
1747  << " vs. " << hit.WireID() << " @" << hit.StartTick()
1748  << " " << hit.LocalIndex() << "/" << hit.Multiplicity()
1749  ;
1750 */
1751  return;
1752  }
1753  if (other_hit.Multiplicity() != hit.Multiplicity()) {
1754 /*
1755  mf::LogError("CC")
1756  << " hit #" << jht << " in the same multiplet as #" << theHit
1757  << " has different multiplicity!"
1758  << "\n hit #" << theHit << ": " << hit
1759  << "\n hit #" << jht << ": " << other_hit;
1760 */
1761  return;
1762  }
1763  // hit is not used by another cluster
1764  if(inClus[jht] != 0) continue;
1765  short arg = (short)(other_hit.PeakTimeMinusRMS(3));
1766  if(arg < loTime) loTime = arg;
1767  arg = (short)(other_hit.PeakTimePlusRMS(3));
1768  if(arg > hiTime) hiTime = arg;
1769  if(jht != theHit) ++nGaus;
1770  hitSep = std::abs(other_hit.PeakTime() - hit.PeakTime()) / other_hit.RMS();
1771  if(jht != theHit && hitSep < 3) ++nclose;
1772  } // jht
1773  // all hits in the multiplet other than this one used?
1774  if(nGaus < 2) return;
1775 
1776  // the hits in this multiplet will have this multiplicity from now on
1777  const short int NewMultiplicity = hit.Multiplicity() + 1 - nGaus;
1778 
1779  if(loTime < 0) loTime = 0;
1780  ++hiTime;
1781  // define a signal shape, fill it with zeros
1782  std::vector<double> signal(hiTime - loTime, 0.);
1783  // now add the Gaussians for each hit
1784  double chgsum = 0.;
1785  for(size_t jht = MultipletRange.first; jht < MultipletRange.second; ++jht) {
1786  recob::Hit const& other_hit = fHits[jht];
1787  if(jht != theHit) {
1788  // hit used in another cluster
1789  if(inClus[jht] != 0) continue;
1790  // declare this hit obsolete
1791  inClus[jht] = -1;
1792  } // jht != theHit
1793  // add up the charge
1794  chgsum += other_hit.Integral();
1795  for(unsigned short time = loTime; time < hiTime; ++time) {
1796  unsigned short indx = time - loTime;
1797  double arg = (other_hit.PeakTime() - (double)time) / other_hit.RMS();
1798  signal[indx] += other_hit.PeakAmplitude() * exp(-0.5 * arg * arg);
1799  } // time
1800  } // jj
1801  // find the average weighted time
1802  double sigsum = 0.;
1803  double sigsumt = 0.;
1804  for(unsigned short time = loTime; time < hiTime; ++time) {
1805  sigsum += signal[time - loTime];
1806  sigsumt += signal[time - loTime] * time;
1807  }
1808  if(sigsum == 0.) {
1809 // mf::LogError("CC")<<"MergeHits: bad sum";
1810  return;
1811  }
1812  double aveTime = sigsumt / sigsum;
1813  // find the RMS
1814  sigsumt = 0.;
1815  for(unsigned short time = loTime; time < hiTime; ++time) {
1816  double dtime = time - aveTime;
1817  sigsumt += signal[time - loTime] * dtime * dtime;
1818  }
1819  const float RMS = std::sqrt(sigsumt / sigsum);
1820  // find the amplitude from the integrated charge and the RMS
1821  const float amplitude = chgsum * chgNorm/ (2.507 * RMS);
1822  // modify the hit "in place" (actually completely overwrite it...)
1823  // TODO a lot of these quantities need revamp!!
1824  fHits[theHit] = recob::Hit(
1825  hit.Channel(),
1826  hit.StartTick(),
1827  hit.EndTick(),
1828  aveTime, // peak_time
1829  hit.SigmaPeakTime(),
1830  RMS, // rms
1831  amplitude, // peak_amplitude
1832  hit.SigmaPeakAmplitude(),
1833  hit.SummedADC(),
1834  chgsum, // hit_integral
1835  hit.SigmaIntegral(),
1836  NewMultiplicity, // multiplicity
1837  0, // local index
1838  6666, // GoodnessOfFit (flag for merged hit)
1839  hit.DegreesOfFreedom(),
1840  hit.View(),
1841  hit.SignalType(),
1842  hit.WireID()
1843  );
1844 /*
1845  if(prt) {
1846  mf::LogVerbatim("CC")
1847  <<" theHit "<<fHits[theHit].WireID().Wire<<":"<<(int)aveTime
1848  <<" RMS "<<std::setprecision(1)<<fHits[theHit].RMS()
1849  <<" chg "<<(int)chgsum<<" Amp "<<(int)fHits[theHit].PeakAmplitude();
1850  }
1851 */
1852  FixMultipletLocalIndices(MultipletRange.first, MultipletRange.second);
1853  didMerge = true;
1854 
1855  } // MergeHits()
1856 
1858  void ClusterCrawlerAlg::FixMultipletLocalIndices
1859  (size_t begin, size_t end, short int multiplicity /* = -1 */)
1860  {
1861  //
1862  // Resets multiplicity and local index of the hits in the range.
1863  // All hits are assumed to be in the same multiplet.
1864  // All hits that are not obsolete are given a multiplicity equal to the
1865  // number of non-obsolete hits in the multiplet, and the local index is
1866  // assigned as an increasing number starting from 0 with the first
1867  // non-obsolete hit on.
1868  //
1869 
1870  // first pass: determine the actual number of hits in the multiplet
1871  if (multiplicity < 0) {
1872  multiplicity = 0;
1873  for (size_t iHit = begin; iHit < end; ++iHit) {
1874  if(inClus[iHit] < 0) continue;
1875  // if (!isHitPresent(iHit)) continue;
1876  ++multiplicity;
1877  } // for
1878  } // if no valid multiplicity is given
1879 
1880  // second pass: assign the correct multiplicity
1881  short int local_index = 0;
1882  for (size_t iHit = begin; iHit < end; ++iHit) {
1883  if(inClus[iHit] < 0) continue;
1884  // if (!isHitPresent(iHit)) continue;
1885 
1886  // copy everything but overwrite the local index and multiplicity
1887  // TODO use a write wrapper!
1888  recob::Hit const& hit = fHits[iHit];
1889  fHits[iHit] = recob::Hit(
1890  hit.Channel(),
1891  hit.StartTick(),
1892  hit.EndTick(),
1893  hit.PeakTime(),
1894  hit.SigmaPeakTime(),
1895  hit.RMS(),
1896  hit.PeakAmplitude(),
1897  hit.SigmaPeakAmplitude(),
1898  hit.SummedADC(),
1899  hit.Integral(),
1900  hit.SigmaIntegral(),
1901  multiplicity, // multiplicity
1902  local_index, // local index
1903  hit.GoodnessOfFit(),
1904  hit.DegreesOfFreedom(),
1905  hit.View(),
1906  hit.SignalType(),
1907  hit.WireID()
1908  );
1909 
1910  ++local_index;
1911  } // for
1912 
1913  } // FixMultipletLocalIndices()
1914 
1916  void ClusterCrawlerAlg::FindStarVertices()
1917  {
1918  // Make 2D vertices with a star topology with short back-to-back
1919  // clusters. Vertices must reside on the US end of the longest
1920  // cluster, so vertex finding uses the End information only.
1921  // This routine is called after all passes are completed
1922  // in the current CTP
1923  if(tcl.size() < 2) return;
1924 
1925  // This code has some issues...
1926  return;
1927 
1928  vtxprt = (fDebugPlane == (int)plane && fDebugHit < 0);
1929  if(vtxprt) {
1930  mf::LogVerbatim("CC")<<"FindStarVertices";
1931  PrintClusters();
1932  }
1933 
1934  unsigned short vtxSizeIn = vtx.size();
1935 
1936  float fvw = 0.;
1937  float fvt = 0.;
1938  float dsl = 0, dth = 0;
1939  float es1 = 0, es2 = 0;
1940  float eth1 = 0, eth2 = 0;
1941  float bt1 = 0, bt2 = 0;
1942  float et1 = 0, et2 = 0;
1943  float bw1 = 0, bw2 = 0;
1944  float ew1 = 0, ew2 = 0;
1945  float lotime = 0, hitime = 0, nwirecut = 0;
1946  unsigned short tclsize = tcl.size();
1947  for(unsigned short it1 = 0; it1 < tclsize - 1; ++it1) {
1948  // ignore obsolete clusters
1949  if(tcl[it1].ID < 0) continue;
1950  if(tcl[it1].CTP != clCTP) continue;
1951  // long dead-straight cluster?
1952  if(tcl[it1].tclhits.size() > 100) {
1953  dth = tcl[it1].BeginAng - tcl[it1].EndAng;
1954  if(std::abs(dth) < 0.1) continue;
1955  }
1956  es1 = tcl[it1].EndSlp;
1957  eth1 = tcl[it1].EndAng;
1958  ew1 = tcl[it1].EndWir;
1959  et1 = tcl[it1].EndTim;
1960  bw1 = tcl[it1].BeginWir;
1961  bt1 = tcl[it1].BeginTim;
1962  for(unsigned short it2 = it1 + 1; it2 < tclsize; ++it2) {
1963  if(tcl[it2].ID < 0) continue;
1964  if(tcl[it2].CTP != clCTP) continue;
1965  // long dead-straight cluster?
1966  if(tcl[it2].tclhits.size() > 100) {
1967  dth = tcl[it2].BeginAng - tcl[it2].EndAng;
1968  if(std::abs(dth) < 0.05) continue;
1969  }
1970  es2 = tcl[it2].EndSlp;
1971  eth2 = tcl[it2].EndAng;
1972  ew2 = tcl[it2].EndWir;
1973  et2 = tcl[it2].EndTim;
1974  bw2 = tcl[it2].BeginWir;
1975  bt2 = tcl[it2].BeginTim;
1976  // require angle difference
1977  dth = std::abs(eth1 - eth2);
1978  if(dth < 0.3) continue;
1979  dsl = es2 - es1;
1980  fvw = (et1 - ew1 * es1 - et2 + ew2 * es2) / dsl;
1981  // intersection within the wire boundaries
1982  if(fvw < ew1 || fvw > bw1) continue;
1983  if(fvw < ew2 || fvw > bw2) continue;
1984  if(vtxprt) mf::LogVerbatim("CC")<<"Chk clusters "<<tcl[it1].ID<<" "<<tcl[it2].ID<<" topo5 vtx wire "<<fvw;
1985  // ensure that the intersection is close to the US end of the longer
1986  // cluster if it is more than 20 hits long
1987  if(tcl[it1].tclhits.size() > tcl[it2].tclhits.size()) {
1988  if(tcl[it1].tclhits.size() > 20) {
1989  nwirecut = 0.5 * tcl[it1].tclhits.size();
1990  if((fvw - ew1) > nwirecut) continue;
1991  }
1992  } else {
1993  if(tcl[it2].tclhits.size() > 20) {
1994  nwirecut = 0.5 * tcl[it2].tclhits.size();
1995  if((fvw - ew2) > nwirecut) continue;
1996  }
1997  }
1998  fvt = et1 + (fvw - ew1) * es1;
1999  // and time boundaries
2000  lotime = 9999;
2001  if(et1 < lotime) lotime = et1;
2002  if(bt1 < lotime) lotime = bt1;
2003  if(et2 < lotime) lotime = et2;
2004  if(bt2 < lotime) lotime = bt2;
2005  hitime = 0;
2006  if(et1 > hitime) hitime = et1;
2007  if(bt1 > hitime) hitime = bt1;
2008  if(et2 > hitime) hitime = et2;
2009  if(bt2 > hitime) hitime = bt2;
2010  if(fvt < lotime || fvt > hitime) continue;
2011  if(vtxprt) mf::LogVerbatim("CC")<<" vertex time "<<fvt<<" lotime "<<lotime<<" hitime "<<hitime;
2012  unsigned int vw = (0.5 + fvw);
2013  // ensure that the vertex is near a hit on both clusters
2014  unsigned int pos1 = 0;
2015  for(unsigned short ii = 0; ii < tcl[it1].tclhits.size(); ++ii) {
2016  if(fHits[tcl[it1].tclhits[ii]].WireID().Wire <= vw) {
2017  if(std::abs(int(fHits[tcl[it1].tclhits[ii]].PeakTime() - fvt)) < 10) pos1 = ii;
2018  break;
2019  }
2020  } // ii
2021  // vertex is not near a hit on cluster 1
2022  if(pos1 == 0) continue;
2023  unsigned short pos2 = 0;
2024  for(unsigned short ii = 0; ii < tcl[it2].tclhits.size(); ++ii) {
2025  if(fHits[tcl[it2].tclhits[ii]].WireID().Wire <= vw) {
2026  if(std::abs(int(fHits[tcl[it2].tclhits[ii]].PeakTime() - fvt)) < 10) pos2 = ii;
2027  break;
2028  }
2029  } // ii
2030  // vertex is not near a hit on cluster 2
2031  if(pos2 == 0) continue;
2032  // ensure we aren't near an existing vertex
2033  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
2034  if(std::abs(fvw - vtx[iv].Wire) < 2 &&
2035  std::abs(fvt - vtx[iv].Time) < 10) continue;
2036  }
2037  // make a new vertex
2038  VtxStore newvx;
2039  newvx.Wire = fvw;
2040  newvx.WireErr = 1;
2041  newvx.Time = fvt;
2042  newvx.TimeErr = 1;
2043  newvx.Topo = 5;
2044  newvx.CTP = clCTP;
2045  newvx.Fixed = false;
2046  vtx.push_back(newvx);
2047  unsigned short ivx = vtx.size() - 1;
2048  if(vtxprt) mf::LogVerbatim("CC")<<" new vertex "<<ivx<<" cluster "<<tcl[it1].ID<<" split pos "<<pos1;
2049  if(!SplitCluster(it1, pos1, ivx)) continue;
2050  tcl[tcl.size()-1].ProcCode += 1000;
2051  tcl[tcl.size()-2].ProcCode += 1000;
2052  if(vtxprt) mf::LogVerbatim("CC")<<" new vertex "<<ivx<<" cluster "<<tcl[it2].ID<<" split pos "<<pos2;
2053  if(!SplitCluster(it2, pos2, ivx)) continue;
2054  tcl[tcl.size()-1].ProcCode += 1000;
2055  tcl[tcl.size()-2].ProcCode += 1000;
2056  FitVtx(ivx);
2057  break;
2058  } // it2
2059  } // it1
2060 
2061  if(vtx.size() > vtxSizeIn) {
2062  // try to split other clusters
2063  VtxClusterSplit();
2064  // try to attach other clusters to it
2065  VertexCluster(vtx.size() - 1);
2066  FitAllVtx(clCTP);
2067  } // new vertex
2068 
2069  if(vtxprt) {
2070  mf::LogVerbatim("CC")<<"Vertices "<<vtx.size();
2071  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
2072  if(vtx[iv].CTP != clCTP) continue;
2073  mf::LogVerbatim("CC")
2074  <<"vtx "<<iv<<" wire "<<vtx[iv].Wire<<" time "<<(int)vtx[iv].Time
2075  <<" NClusters "<<vtx[iv].NClusters<<" topo "<<vtx[iv].Topo;
2076  }
2077  PrintClusters();
2078  }
2079 
2080  } // FindStarVertices()
2081 
2083  void ClusterCrawlerAlg::VertexCluster(unsigned short iv)
2084  {
2085  // try to attach clusters to the specified vertex
2086  if(vtx[iv].NClusters == 0) return;
2087 
2088  short dwb, dwe, dtb, dte;
2089  bool sigOK;
2090 
2091 // vtxprt = (fDebugPlane >= 0 && fDebugWire == 3333);
2092 
2093  for(unsigned short icl = 0; icl < tcl.size(); ++icl) {
2094  if(tcl[icl].ID < 0) continue;
2095  if(tcl[icl].CTP != vtx[iv].CTP) continue;
2096 
2097  dwb = vtx[iv].Wire - tcl[icl].BeginWir;
2098  dtb = vtx[iv].Time - tcl[icl].BeginTim;
2099  dwe = vtx[iv].Wire - tcl[icl].EndWir;
2100  dte = vtx[iv].Time - tcl[icl].EndTim;
2101 
2102  float drb = dwb * dwb + dtb * dtb;
2103  float dre = dwe * dwe + dte * dte;
2104 
2105  bool bCloser = (drb < dre);
2106 
2107  // ignore clusters in showers
2108  if(bCloser) {
2109  if(tcl[icl].BeginChgNear > fChgNearCut) continue;
2110  } else {
2111  if(tcl[icl].EndChgNear > fChgNearCut) continue;
2112  }
2113 
2114  if(vtxprt) mf::LogVerbatim("CC")<<"VertexCluster: Try icl ID "<<tcl[icl].ID<<" w vtx "<<iv<<" dwb "<<dwb<<" dwe "<<dwe<<" drb "<<drb<<" dre "<<dre<<" Begin closer? "<<bCloser;
2115 
2116  if(tcl[icl].BeginVtx < 0 && bCloser && dwb > -3 && dwb < 3 && tcl[icl].EndVtx != iv) {
2117  sigOK = ChkSignal(tcl[icl].BeginWir, tcl[icl].BeginTim, vtx[iv].Wire, vtx[iv].Time);
2118  if(vtxprt) mf::LogVerbatim("CC")<<" Attach cluster Begin to vtx? "<<iv<<" sigOK "<<sigOK;
2119  if(sigOK) {
2120  if(vtxprt) mf::LogVerbatim("CC")<<" check ClusterVertexChi "<<ClusterVertexChi(icl, 0, iv);
2121  if(ClusterVertexChi(icl, 0, iv) < fVertex2DCut) {
2122  // do a fit and check the vertex error
2123  tcl[icl].BeginVtx = iv;
2124  FitVtx(iv);
2125  if(vtx[iv].ChiDOF > fVertex2DCut || vtx[iv].WireErr > fVertex2DWireErrCut) {
2126  tcl[icl].BeginVtx = -99;
2127  FitVtx(iv);
2128  }
2129  } // good DoCA
2130  } // sigOK
2131  } // check BEGIN
2132 
2133  if(tcl[icl].EndVtx < 0 && !bCloser && dwe > -3 && dwe < 3 && tcl[icl].BeginVtx != iv) {
2134  sigOK = ChkSignal(tcl[icl].EndWir, tcl[icl].EndTim, vtx[iv].Wire, vtx[iv].Time);
2135  if(vtxprt) mf::LogVerbatim("CC")<<" Attach cluster End to vtx? "<<iv<<" sigOK "<<sigOK;
2136  if(sigOK) {
2137  if(vtxprt) mf::LogVerbatim("CC")<<" check ClusterVertexChi "<<ClusterVertexChi(icl, 1, iv);
2138  if(ClusterVertexChi(icl, 1, iv) < 3) {
2139  // do a fit and check the vertex error
2140  tcl[icl].EndVtx = iv;
2141  FitVtx(iv);
2142  if(vtx[iv].ChiDOF > fVertex2DCut || vtx[iv].WireErr > fVertex2DWireErrCut) {
2143  tcl[icl].EndVtx = -99;
2144  FitVtx(iv);
2145  }
2146  } // good DoCA
2147  } // sigOK
2148  } // check END
2149  } // icl
2150  } // VertexCluster
2151 
2153  void ClusterCrawlerAlg::FitAllVtx(CTP_t inCTP)
2154  {
2155 
2156  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
2157  if(vtx[iv].CTP != inCTP) continue;
2158  FitVtx(iv);
2159  }
2160 
2161  } // FitAllVtx
2162 
2163 /*
2165  void ClusterCrawlerAlg::TagClusters()
2166  {
2167  // tag as shower-like or track-like
2168 
2169  if(tcl.size() < 2) return;
2170 
2171  unsigned short icl, jcl, end;
2172 
2173  std::vector<std::array<unsigned short, 2>> nClose;
2174  std::vector<std::array<float, 2>> sum;
2175  std::array<unsigned short, 2> zeros = {0, 0};
2176  std::array<float, 2> fzeros = {0, 0};
2177 
2178  mf::LogVerbatim("CC")<<"TagClusters "<<tcl.size()<<"\n";
2179 
2180  float wire, time, doca, maxDoCA = 20;
2181 
2182  unsigned short indx = 0;
2183  for(icl = 0; icl < tcl.size(); ++icl) {
2184  if(tcl[icl].ID < 0) continue;
2185  if(tcl[icl].CTP != clCTP) continue;
2186  nClose.push_back(zeros);
2187  sum.push_back(fzeros);
2188  for(end = 0; end < 2; ++end) {
2189  if(end == 0) {
2190  wire = tcl[icl].BeginWir;
2191  time = tcl[icl].BeginTim;
2192  } else {
2193  wire = tcl[icl].EndWir;
2194  time = tcl[icl].EndTim;
2195  };
2196  for(jcl = 0; jcl < tcl.size(); ++jcl) {
2197  if(icl == jcl) continue;
2198  if(tcl[jcl].ID < 0) continue;
2199  if(tcl[jcl].CTP != clCTP) continue;
2200  doca = DoCA(jcl, end, wire, time);
2201  if(doca < maxDoCA) {
2202  ++nClose[indx][end];
2203  sum[indx][end] += doca;
2204  }
2205  } // jcl
2206  } // end
2207  if(nClose[indx][0] > 0) sum[indx][0] /= (float)nClose[indx][0];
2208  if(nClose[indx][1] > 0) sum[indx][0] /= (float)nClose[indx][1];
2209  mf::LogVerbatim("CC")<<"Cluster "<<tcl[icl].ID<<" nClose "<<nClose[indx][0]<<" "<<nClose[indx][1]<<" Ave "<<(int)sum[indx][0]<<" "<<(int)sum[indx][1];
2210  ++indx;
2211  } // icl
2212 
2213  } // TagClusters
2214 */
2215 
2217  void ClusterCrawlerAlg::FindVertices()
2218  {
2219  // try to make 2D vertices
2220 
2221  if(tcl.size() < 2) return;
2222 
2223  // sort clusters starting with the longest
2224  std::vector<CluLen> clulens;
2225  CluLen clulen;
2226  for(unsigned short icl = 0; icl < tcl.size(); ++icl) {
2227  if(tcl[icl].ID < 0) continue;
2228  if(tcl[icl].CTP != clCTP) continue;
2229  if(tcl[icl].BeginVtx >= 0 && tcl[icl].EndVtx >= 0) continue;
2230  clulen.index = icl;
2231  clulen.length = tcl[icl].tclhits.size();
2232  clulens.push_back(clulen);
2233  }
2234  if(clulens.size() == 0) return;
2235  std::sort (clulens.begin(),clulens.end(), greaterThan);
2236 
2237  float nwires = fNumWires;
2238  float maxtime = fMaxTime;
2239 
2240  unsigned short vtxSizeIn = vtx.size();
2241 
2242  vtxprt = (fDebugPlane == (short)plane && fDebugHit < 0);
2243  if(vtxprt) {
2244  mf::LogVerbatim("CC")<<"FindVertices plane "<<plane<<" pass "<<pass;
2245  PrintClusters();
2246  }
2247 
2248  float es1 = 0, eth1 = 0, ew1 = 0, et1 = 0;
2249  float bs1 = 0, bth1 = 0, bw1 = 0, bt1 = 0;
2250  float es2 = 0, eth2 = 0, ew2 = 0, et2 = 0;
2251  float bs2 = 0, bth2 = 0, bw2 = 0, bt2 = 0;
2252  float dth = 0, dsl = 0, fvw = 0, fvt = 0;
2253  float angcut = 0;
2254  unsigned int vw = 0, pass1, pass2, ii1, it1, ii2, it2;
2255  bool SigOK = false;
2256  for(ii1 = 0; ii1 < clulens.size() - 1; ++ii1) {
2257  it1 = clulens[ii1].index;
2258  es1 = tcl[it1].EndSlp;
2259  eth1 = tcl[it1].EndAng;
2260  ew1 = tcl[it1].EndWir;
2261  et1 = tcl[it1].EndTim;
2262  bs1 = tcl[it1].BeginSlp;
2263  bth1 = tcl[it1].BeginAng;
2264  bw1 = tcl[it1].BeginWir;
2265  bt1 = tcl[it1].BeginTim;
2266  pass1 = tcl[it1].ProcCode - 10 *(tcl[it1].ProcCode / 10);
2267  for(ii2 = ii1 + 1; ii2 < clulens.size(); ++ii2) {
2268  it2 = clulens[ii2].index;
2269  // try to attach cluster to existing vertices at either end
2270  ClusterVertex(it2);
2271  // ignore if both clusters are short
2272  if(tcl[it1].tclhits.size() < 5 &&
2273  tcl[it2].tclhits.size() < 5) continue;
2274  es2 = tcl[it2].EndSlp;
2275  eth2 = tcl[it2].EndAng;
2276  ew2 = tcl[it2].EndWir;
2277  et2 = tcl[it2].EndTim;
2278  bs2 = tcl[it2].BeginSlp;
2279  bth2 = tcl[it2].BeginAng;
2280  bw2 = tcl[it2].BeginWir;
2281  bt2 = tcl[it2].BeginTim;
2282  pass2 = tcl[it2].ProcCode - 10 *(tcl[it2].ProcCode / 10);
2283  if(pass1 < pass2) {
2284  angcut = fKinkAngCut[pass2];
2285  } else {
2286  angcut = fKinkAngCut[pass1];
2287  }
2288  // topo 1: check for vtx US of the ends of both clusters
2289  dth = fabs(eth1 - eth2);
2290  if(tcl[it1].EndVtx < 0 && tcl[it2].EndVtx < 0 && dth > 0.1) {
2291  dsl = es2 - es1;
2292  // find vertex wire and vertex time in float precision (fvw, fvt)
2293  fvw = (et1 - ew1 * es1 - et2 + ew2 * es2) / dsl;
2294  // vertex wire in the detector?
2295  if(fvw > 0. && fvw < nwires) {
2296  // require vtx in the range of wires with hits AND
2297  vw = (0.5 + fvw);
2298  // vtx US of both clusters AND
2299  // vtx not too far US of both clusters
2300  if(vw >= fFirstWire - 1 &&
2301  fvw <= ew1 + 3 && fvw <= ew2 + 3 &&
2302  fvw > (ew1 - 10) && fvw > (ew2 - 10) ) {
2303  fvt = et1 + (fvw - ew1) * es1;
2304  if(vtxprt) mf::LogVerbatim("CC")<<"Chk clusters topo1 "<<tcl[it1].ID<<" "<<tcl[it2].ID<<" vtx wire "<<vw<<" time "<<(int)fvt<<" dth "<<dth;
2305  if(fvt > 0 && fvt < maxtime) {
2306  // Vertex wire US of cluster ends and time in the detector.
2307  // Check for signal at the vertex position and adjust the vertex by 1 wire
2308  // if necessary
2309  SigOK = ChkSignal(vw, fvt, vw, fvt);
2310  if(!SigOK) {
2311  fvw += 1.;
2312  vw = (0.5 + fvw);
2313  SigOK = ChkSignal(vw, fvt, vw, fvt);
2314  }
2315  // Check this against existing vertices and update
2316  if(SigOK) ChkVertex(fvw, fvt, it1, it2, 1);
2317  } // fvt in detector
2318  } // vw topo 1 check
2319  } // fvw in detector
2320  } // topo 1
2321  // topo 2: check for vtx US of it1 and DS of it2
2322  dth = std::abs(eth1 - bth2);
2323  if(tcl[it1].EndVtx < 0 && tcl[it2].BeginVtx < 0 && dth > angcut) {
2324  dsl = bs2 - es1;
2325  if(fabs(ew1 - bw2) < 3 && fabs(et1 - bt2) < 500) {
2326  fvw = 0.5 * (ew1 + bw2);
2327  } else {
2328  fvw = (et1 - ew1 * es1 - bt2 + bw2 * bs2) / dsl;
2329  }
2330  if(fvw > 0 && fvw < nwires) {
2331  // vertex wire in the detector
2332  vw = (0.5 + fvw);
2333  // require vtx US of cluster 1 End AND
2334  // vtx DS of cluster 2 Begin
2335  if(fvw <= ew1 + 2 && fvw >= bw2 - 2) {
2336  fvt = et1 + (vw - ew1) * es1;
2337  fvt += bt2 + (vw - bw2) * bs2;
2338  fvt /= 2;
2339  if(vtxprt) mf::LogVerbatim("CC")<<"Chk clusters topo2 "<<tcl[it1].ID<<" "<<tcl[it2].ID<<" vtx wire "<<vw<<" time "<<(int)fvt<<" dth "<<dth;
2340  if(fvt > 0. && fvt < maxtime) {
2341  ChkVertex(fvw, fvt, it1, it2, 2);
2342  } // fvt in detector
2343  } // vw topo 2 check
2344  } // fvw in detector
2345  } // topo 2
2346  // topo 3: check for vtx DS of it1 and US of it2
2347  dth = std::abs(bth1 - eth2);
2348  if(tcl[it1].BeginVtx < 0 && tcl[it2].EndVtx < 0 && dth > angcut) {
2349  dsl = bs1 - es2;
2350  if(fabs(bw1 - ew2) < 3 && fabs(bt1 - et2) < 500) {
2351  fvw = 0.5 * (bw1 + ew2);
2352  } else {
2353  fvw = (et2 - ew2 * es2 - bt1 + bw1 * bs1) / dsl;
2354  }
2355  if(fvw > 0 && fvw < nwires) {
2356  vw = (0.5 + fvw);
2357  // require vtx US of cluster 2 Begin AND
2358  // vtx DS of cluster 1 End
2359  if(fvw <= ew2 + 2 && fvw >= bw1 - 2) {
2360  fvt = et2 + (fvw - ew2) * es2;
2361  fvt += bt1 + (fvw - bw1) * es1;
2362  fvt /= 2;
2363  if(vtxprt) mf::LogVerbatim("CC")<<"Chk clusters topo3 "<<tcl[it1].ID<<" "<<tcl[it2].ID<<" vtx wire "<<vw<<" time "<<(int)fvt<<" dth "<<dth;
2364  if(fvt > 0. && fvt < maxtime) {
2365  ChkVertex(fvw, fvt, it1, it2, 3);
2366  } // fvt in detector
2367  } // vw topo 3 check
2368  } // fvw in detector
2369  } // topo 3
2370  // topo 4: check for vtx DS of it1 and DS of it2
2371  dth = std::abs(bth1 - bth2);
2372  if(tcl[it1].BeginVtx < 0 && tcl[it2].BeginVtx < 0 && dth > 0.1) {
2373  dsl = bs2 - bs1;
2374  // find vertex wire and vertex time in float precision (fvw, fvt)
2375  // convert to integer if within the detector (vw, vt)
2376  fvw = (bt1 - bw1 * bs1 - bt2 + bw2 * bs2) / dsl;
2377  if(vtxprt) mf::LogVerbatim("CC")<<"Chk clusters topo4 "<<bw1<<":"<<(int)bt1<<" "<<bw2<<":"<<(int)bt2<<" fvw "<<fvw<<" nwires "<<nwires;
2378  if(fvw > 0 && fvw < nwires) {
2379  // vertex wire in the detector
2380  vw = (0.5 + fvw);
2381  // require vtx in the range of wires with hits AND vtx DS of both clusters AND
2382  // vtx not too far DS of both clusters
2383  float dwcut = 10;
2384  if(tcl[it1].tclhits.size() < 2 * dwcut) dwcut = tcl[it1].tclhits.size()/2;
2385  if(tcl[it2].tclhits.size() < 2 * dwcut) dwcut = tcl[it2].tclhits.size()/2;
2386  if(fvw <= fLastWire + 1 &&
2387  fvw >= bw1 - dwcut && fvw <= bw1 + dwcut &&
2388  fvw >= bw2 - dwcut && fvw <= bw1 + dwcut) {
2389  fvt = bt1 + (fvw - bw1) * bs1;
2390  if(vtxprt) mf::LogVerbatim("CC")<<" vtx wire "<<vw<<" time "<<fvt<<" dth "<<dth<<" dwcut "<<dwcut;
2391  if(fvt > 0. && fvt < maxtime) {
2392  // vertex wire US of cluster ends and time in the detector
2393  // Check for signal at the vertex position and adjust the vertex by 1 wire
2394  // if necessary
2395  SigOK = ChkSignal(vw, fvt, vw, fvt);
2396  if(!SigOK) {
2397  fvw -= 1.;
2398  vw = (0.5 + fvw);
2399  SigOK = ChkSignal(vw, fvt, vw, fvt);
2400  }
2401  // Check this against existing vertices and update
2402  if(SigOK) ChkVertex(fvw, fvt, it1, it2, 4);
2403  } // fvt in detector
2404  } // vw topo 4 check
2405  } // fvw in detector
2406  } // topo4
2407  } // it2
2408  } // it1
2409 
2410  // Fix vertices where both ends of a cluster are assigned to the
2411  // same vertex. This can happen with short clusters
2412  for(unsigned short it = 0; it < tcl.size(); ++it) {
2413  if(tcl[it].ID < 0) continue;
2414  if(tcl[it].CTP != clCTP) continue;
2415  if(tcl[it].BeginVtx > -1 && tcl[it].BeginVtx == tcl[it].EndVtx ) {
2416  unsigned short iv = tcl[it].BeginVtx;
2417  float dwir = tcl[it].BeginWir - vtx[iv].Wire;
2418  float dtim = tcl[it].BeginTim - vtx[iv].Time;
2419  float rbeg = dwir * dwir + dtim * dtim;
2420  dwir = tcl[it].EndWir - vtx[iv].Wire;
2421  dtim = tcl[it].EndTim - vtx[iv].Time;
2422  float rend = dwir * dwir + dtim * dtim;
2423  if(rend < rbeg) {
2424  tcl[it].EndVtx = -99;
2425  } else {
2426  tcl[it].BeginVtx = -99;
2427  }
2428  } // tcl[it].BeginVtx == tcl[it].EndVtx
2429  } // it
2430 
2431  if(vtx.size() > vtxSizeIn) FitAllVtx(clCTP);
2432 
2433  // "delete" any vertices that have only one cluster
2434  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
2435  if(vtx[ivx].CTP != clCTP) continue;
2436  if(vtx[ivx].NClusters == 1) {
2437  vtx[ivx].NClusters = 0;
2438  for(unsigned short icl = 0; icl < tcl.size(); ++icl) {
2439  if(tcl[icl].CTP != clCTP) continue;
2440  if(tcl[icl].BeginVtx == ivx) {
2441  tcl[icl].BeginVtx = -99;
2442  break;
2443  }
2444  if(tcl[icl].EndVtx == ivx) {
2445  tcl[icl].EndVtx = -99;
2446  break;
2447  }
2448  } // icl
2449  } // vtx[ivx].NClusters == 1
2450  } // ivx
2451 
2452 
2453  } // FindVertices()
2454 
2456  void ClusterCrawlerAlg::ClusterVertex(unsigned short it)
2457  {
2458  // try to attach cluster it to an existing vertex
2459 
2460  if(vtx.size() == 0) return;
2461 
2462  unsigned short iv, jv;
2463  short dwib, dwjb, dwie, dwje;
2464  bool matchEnd, matchBegin;
2465 
2466 // if(vtxprt) mf::LogVerbatim("CC")<<"ClusterVertex: Try to attach cluster "<<tcl[it].ID<<" to existing vertices";
2467 
2468  for(iv = 0; iv < vtx.size(); ++iv) {
2469  // ignore vertices in the wrong cryostat/TPC/Plane
2470  if(vtx[iv].CTP != clCTP) continue;
2471  // ignore deleted vertices
2472  if(vtx[iv].NClusters == 0) continue;
2473  // determine which end to match - begin or end. Handle short tracks
2474  matchEnd = false; matchBegin = false;
2475  if(tcl[it].tclhits.size() < 6) {
2476  // See which end is closer to this vertex vs other vertices
2477  dwib = std::abs(vtx[iv].Wire - tcl[it].BeginWir);
2478  if(dwib > 2) dwib = 2;
2479  dwie = std::abs(vtx[iv].Wire - tcl[it].EndWir);
2480  if(dwie > 2) dwie = 2;
2481  dwjb = 999; dwje = 999;
2482  for(jv = 0; jv < vtx.size(); ++jv) {
2483  if(iv == jv) continue;
2484  if(std::abs(vtx[jv].Time - tcl[it].BeginTim) < 50) {
2485  if(std::abs(vtx[jv].Wire - tcl[it].BeginWir) < dwjb)
2486  dwjb = std::abs(vtx[jv].Wire - tcl[it].BeginWir);
2487  } // std::abs(vtx[jv].Time - tcl[it].BeginTim) < 50
2488  if(std::abs(vtx[jv].Time - tcl[it].EndTim) < 50) {
2489  if(std::abs(vtx[jv].Wire - tcl[it].EndWir) < dwje)
2490  dwje = std::abs(vtx[jv].Wire - tcl[it].EndWir);
2491  } // std::abs(vtx[jv].Time - tcl[it].EndTim) < 50
2492  } // jv
2493  matchEnd = tcl[it].EndVtx != iv &&
2494  dwie < 3 && dwie < dwje && dwie < dwib;
2495  matchBegin = tcl[it].BeginVtx != iv &&
2496  dwib < 3 && dwib < dwjb && dwib < dwie;
2497 // if(vtxprt) mf::LogVerbatim("CC")<<" dwib "<<dwib<<" dwie " <<dwie<<" dwjb "<<dwjb<<" dwje "<<dwje;
2498  } else {
2499  matchEnd = tcl[it].EndVtx < 0 && vtx[iv].Wire <= tcl[it].EndWir + 2;
2500  matchBegin = tcl[it].BeginVtx < 0 && vtx[iv].Wire >= tcl[it].BeginWir - 2;
2501  }
2502  if(matchEnd) {
2503  if(vtxprt) mf::LogVerbatim("CC")<<" Match End chi "<<ClusterVertexChi(it, 1, iv)<<" to vtx "<<iv
2504  <<" signalOK "<<ChkSignal(vtx[iv].Wire, vtx[iv].Time, tcl[it].EndWir, tcl[it].EndTim);
2505  if(ClusterVertexChi(it, 1, iv) < fVertex2DCut && ChkSignal(vtx[iv].Wire, vtx[iv].Time, tcl[it].EndWir, tcl[it].EndTim)) {
2506  // good match
2507  tcl[it].EndVtx = iv;
2508  // re-fit it
2509  FitVtx(iv);
2510  if(vtxprt) mf::LogVerbatim("CC")<<" Add End "<<tcl[it].ID<<" to vtx "<<iv<<" NClusters "<<vtx[iv].NClusters;
2511  if(vtx[iv].ChiDOF < fVertex2DCut && vtx[iv].WireErr < fVertex2DWireErrCut) return;
2512  if(vtxprt) mf::LogVerbatim("CC")<<" Bad fit. ChiDOF "<<vtx[iv].ChiDOF<<" WireErr "<<vtx[iv].WireErr<<" Undo it";
2513  tcl[it].EndVtx = -99;
2514  FitVtx(iv);
2515  } // tChi < 3
2516  } // matchEnd
2517 
2518  if(matchBegin) {
2519  if(vtxprt) mf::LogVerbatim("CC")<<" Match Begin chi "<<ClusterVertexChi(it, 0, iv)<<" to vtx "<<iv
2520  <<" signalOK "<<ChkSignal(vtx[iv].Wire, vtx[iv].Time, tcl[it].BeginWir, tcl[it].BeginTim);
2521  if(ClusterVertexChi(it, 0, iv) < fVertex2DCut && ChkSignal(vtx[iv].Wire, vtx[iv].Time, tcl[it].BeginWir, tcl[it].BeginTim)) {
2522  // good match
2523  tcl[it].BeginVtx = iv;
2524  // re-fit it
2525  FitVtx(iv);
2526  if(vtxprt) mf::LogVerbatim("CC")<<" Add Begin "<<tcl[it].ID<<" to vtx "<<iv<<" NClusters "<<vtx[iv].NClusters;
2527  if(vtx[iv].ChiDOF < fVertex2DCut && vtx[iv].WireErr < fVertex2DWireErrCut) return;
2528  if(vtxprt) mf::LogVerbatim("CC")<<" Bad fit. ChiDOF "<<vtx[iv].ChiDOF<<" WireErr "<<vtx[iv].WireErr<<" Undo it";
2529  tcl[it].BeginVtx = -99;
2530  FitVtx(iv);
2531  } // tChi < 3
2532  } // matchBegin
2533  } // iv
2534  } // ClusterVertex()
2535 
2536 
2537 
2539  void ClusterCrawlerAlg::ChkVertex(float fvw, float fvt, unsigned short it1, unsigned short it2, short topo)
2540  {
2541  // Checks the vertex (vw, fvt) against the existing set of vertices.
2542  // If there a match, clusters it1 and/or it2 are associated with it
2543  // if there is signal between the existing vertex and the start of
2544  // the cluster. The topo flag indicates the type of vertex that was
2545  // found: 1 = US of it1 && US of it2, 2 = US of it1 && DS of it2,
2546  // 3 = DS of it1 && US of it2, 4 = DS of it1 and DS of it2.
2547  // didit is set true if a cluster is attached to a (new or old) vertex
2548 
2549  if(vtxprt) mf::LogVerbatim("CC")<<" ChkVertex "
2550  <<tcl[it1].EndWir<<":"<<(int)tcl[it1].EndTim<<" - "<<tcl[it1].BeginWir<<":"<<(int)tcl[it1].BeginTim<<" and "
2551  <<tcl[it2].EndWir<<":"<<(int)tcl[it2].EndTim<<" - "<<tcl[it2].BeginWir<<":"<<(int)tcl[it2].BeginTim
2552  <<" topo "<<topo<<" fvw "<<fvw<<" fvt "<<fvt;
2553 
2554  unsigned int vw = (unsigned int)(0.5 + fvw);
2555  unsigned int iht;
2556 
2557  // don't make vertices using short tracks that are close to
2558  // long straight clusters. These are most likely due to numerous
2559  // short delta ray clusters
2560  if(tcl[it1].tclhits.size() < 10 && tcl[it2].tclhits.size() < 10) {
2561  for(unsigned short it = 0; it < tcl.size(); ++it) {
2562  if(it == it1 || it == it2) continue;
2563  if(tcl[it].ID < 0) continue;
2564  if(tcl[it].CTP != clCTP) continue;
2565  if(tcl[it].tclhits.size() < 100) continue;
2566  if(std::abs(tcl[it].BeginAng - tcl[it].EndAng) > 0.1) continue;
2567  // don't reject because it is near the end of a long cluster
2568  if(vw < tcl[it].EndWir + 5) continue;
2569  if(vw > tcl[it].BeginWir - 5) continue;
2570  for(unsigned short ii = 0; ii < tcl[it].tclhits.size(); ++ii) {
2571  iht = tcl[it].tclhits[ii];
2572  if(fHits[iht].WireID().Wire <= vw) {
2573  if(std::abs(fHits[iht].PeakTime() - fvt) < 60) return;
2574  } // fHits[iht].WireID().Wire <= vWire
2575  } // ii
2576  } // it
2577  }
2578 
2579 // if(vtxprt) mf::LogVerbatim("CC")<<" passed cut1";
2580 
2581  // check vertex and clusters for proximity to existing vertices
2582  unsigned short nFitOK = 0;
2583  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
2584  if(vtx[iv].CTP != clCTP) continue;
2585  // make this a really loose cut since the errors on the prospective vertex aren't known yet
2586  if(PointVertexChi(fvw, fvt, iv) > 300) continue;
2587  if(vtxprt) mf::LogVerbatim("CC")<<" vtx "<<iv<<" PointVertexChi "<<PointVertexChi(fvw, fvt, iv);
2588  // got a match. Check the appropriate cluster end and attach
2589  if( (topo == 1 || topo == 2) && tcl[it1].EndVtx < 0) {
2590  if(ChkSignal(vw, fvt, tcl[it1].EndWir, tcl[it1].EndTim)) {
2591  // try to fit
2592  tcl[it1].EndVtx = iv;
2593  FitVtx(iv);
2594  if(vtxprt) mf::LogVerbatim("CC")<<" FitVtx "<<iv<<" WireErr "<<vtx[iv].WireErr<<" ChiDOF "<<vtx[iv].ChiDOF;
2595  if(vtx[iv].WireErr < fVertex2DWireErrCut && vtx[iv].ChiDOF < 5) {
2596  ++nFitOK;
2597  } else {
2598  // bad fit
2599  tcl[it1].EndVtx = -99;
2600  FitVtx(iv);
2601  } // check fit
2602  } // ChkSignal
2603 // if(vtxprt) mf::LogVerbatim("CC")<<" 12 Attach cl "<<tcl[it1].ID<<" to vtx? "<<iv;
2604  } else if( (topo == 3 || topo == 4) && tcl[it1].BeginVtx < 0) {
2605  if(ChkSignal(vw, fvt, tcl[it1].BeginWir, tcl[it1].BeginTim)) {
2606  tcl[it1].BeginVtx = iv;
2607  FitVtx(iv);
2608  if(vtxprt) mf::LogVerbatim("CC")<<" FitVtx "<<iv<<" WireErr "<<vtx[iv].WireErr<<" ChiDOF "<<vtx[iv].ChiDOF;
2609  if(vtx[iv].WireErr < fVertex2DWireErrCut && vtx[iv].ChiDOF < 5) {
2610  ++nFitOK;
2611  } else {
2612  // bad fit
2613  tcl[it1].BeginVtx = -99;
2614  FitVtx(iv);
2615  } // bad fit
2616  } // ChkSignal
2617 // if(vtxprt) mf::LogVerbatim("CC")<<" 34 Attach cl "<<tcl[it1].ID<<" to vtx? "<<iv;
2618  } // cluster it2
2619  if( (topo == 1 || topo == 3) && tcl[it2].EndVtx < 0) {
2620  if(ChkSignal(vw, fvt, tcl[it2].EndWir, tcl[it2].EndTim)) {
2621  tcl[it2].EndVtx = iv;
2622  FitVtx(iv);
2623  if(vtxprt) mf::LogVerbatim("CC")<<" FitVtx "<<iv<<" WireErr "<<vtx[iv].WireErr<<" ChiDOF "<<vtx[iv].ChiDOF;
2624  if(vtx[iv].WireErr < fVertex2DWireErrCut && vtx[iv].ChiDOF < 5) {
2625  ++nFitOK;
2626  } else {
2627  // bad fit
2628  tcl[it2].EndVtx = -99;
2629  FitVtx(iv);
2630  } // bad fit
2631  } // ChkSignal
2632  } else if( (topo == 2 || topo == 4) && tcl[it2].BeginVtx < 0) {
2633  if(ChkSignal(vw, fvt, tcl[it2].BeginWir, tcl[it2].BeginTim)) {
2634  tcl[it2].BeginVtx = iv;
2635  FitVtx(iv);
2636  if(vtxprt) mf::LogVerbatim("CC")<<" FitVtx "<<iv<<" WireErr "<<vtx[iv].WireErr<<" ChiDOF "<<vtx[iv].ChiDOF;
2637  if(vtx[iv].WireErr < fVertex2DWireErrCut && vtx[iv].ChiDOF < 5) {
2638  ++nFitOK;
2639  } else {
2640  // bad fit
2641  tcl[it2].BeginVtx = -99;
2642  FitVtx(iv);
2643  } // bad fit
2644  } // ChkSignal
2645  } // cluster it2
2646  if(nFitOK > 0) {
2647  if(vtxprt) mf::LogVerbatim("CC")<<" Attached "<<nFitOK<<" clusters to vertex "<<iv;
2648  return;
2649  }
2650  } // iv
2651 
2652 // if(vtxprt) mf::LogVerbatim("CC")<<" passed cut2";
2653 
2654  // no match to existing vertices. Ensure that there is a wire signal between
2655  // the vertex and the appropriate ends of the clusters
2656  bool SigOK = false;
2657  if(topo == 1 || topo == 2) {
2658  SigOK = ChkSignal(vw, fvt, tcl[it1].EndWir, tcl[it1].EndTim);
2659 // if(vtxprt) mf::LogVerbatim("CC")<<" chk1 vtx W:T "<<vw<<":"<<(int)fvt<<" "<<tcl[it1].EndWir<<":"<<(int)tcl[it1].EndTim<<" SigOK "<<SigOK;
2660  } else {
2661  SigOK = ChkSignal(vw, fvt, tcl[it1].BeginWir, tcl[it1].BeginTim);
2662 // if(vtxprt) mf::LogVerbatim("CC")<<" chk1 vtx W:T "<<vw<<":"<<(int)fvt<<" "<<tcl[it1].BeginWir<<":"<<(int)tcl[it1].BeginTim<<" SigOK "<<SigOK;
2663  }
2664 // if(vtxprt) mf::LogVerbatim("CC")<<" SigOK it1 "<<SigOK;
2665  if(!SigOK) return;
2666 
2667  if(topo == 1 || topo == 3) {
2668  SigOK = ChkSignal(vw, fvt, tcl[it2].EndWir, tcl[it2].EndTim);
2669 // if(vtxprt) mf::LogVerbatim("CC")<<" chk2 vtx W:T "<<vw<<":"<<(int)fvt<<" "<<tcl[it2].EndWir<<":"<<(int)tcl[it2].EndTim<<" SigOK "<<SigOK;
2670  } else {
2671  SigOK = ChkSignal(vw, fvt, tcl[it2].BeginWir, tcl[it2].BeginTim);
2672 // if(vtxprt) mf::LogVerbatim("CC")<<" chk2 vtx W:T "<<vw<<":"<<(int)fvt<<" "<<tcl[it2].BeginWir<<":"<<(int)tcl[it2].BeginTim<<" SigOK "<<SigOK;
2673  }
2674 // if(vtxprt) mf::LogVerbatim("CC")<<" SigOK it2 "<<SigOK;
2675  if(!SigOK) return;
2676 
2677  VtxStore newvx;
2678  newvx.Wire = vw;
2679  newvx.Time = fvt;
2680  newvx.Topo = topo;
2681  newvx.CTP = clCTP;
2682  newvx.Fixed = false;
2683  vtx.push_back(newvx);
2684  unsigned short iv = vtx.size() - 1;
2685  if(topo == 1 || topo == 2) {
2686  if(tcl[it1].EndVtx >= 0) {
2687 // mf::LogError("CC")<<"ChkVertex: Coding error trying to make new vtx "<<iv<<"\n";
2688  vtx.pop_back();
2689  return;
2690  }
2691  tcl[it1].EndVtx = iv;
2692  } else {
2693  if(tcl[it1].BeginVtx >= 0) {
2694 // mf::LogError("CC")<<"ChkVertex: Coding error trying to make new vtx "<<iv<<"\n";
2695  vtx.pop_back();
2696  return;
2697  }
2698  tcl[it1].BeginVtx = iv;
2699  }
2700  if(topo == 1 || topo == 3) {
2701  if(tcl[it2].EndVtx >= 0) {
2702 // mf::LogError("CC")<<"ChkVertex: Coding error trying to make new vtx "<<iv<<"\n";
2703  vtx.pop_back();
2704  return;
2705  }
2706  tcl[it2].EndVtx = iv;
2707  } else {
2708  if(tcl[it2].BeginVtx >= 0) {
2709 // mf::LogError("CC")<<"ChkVertex: Coding error trying to make new vtx "<<iv<<"\n";
2710  vtx.pop_back();
2711  return;
2712  }
2713  tcl[it2].BeginVtx = iv;
2714  }
2715  // fit it
2716  FitVtx(iv);
2717  // reject it if the fit is bad
2718  if(vtx[iv].ChiDOF < 5 && vtx[iv].WireErr < fVertex2DWireErrCut && vtx[iv].TimeErr < 20) {
2719  if(vtxprt) mf::LogVerbatim("CC")<<" New vtx "<<iv<<" in plane "<<plane<<" topo "<<topo<<" cls "<<tcl[it1].ID<<" "<<tcl[it2].ID<<" W:T "<<(int)vw<<":"<<(int)fvt<<" NClusters "<<vtx[iv].NClusters;
2720  // Try to refine the cluster hits and vertex position
2721  if(fRefineVertexClusters) RefineVertexClusters(iv);
2722  } else {
2723  if(vtxprt) mf::LogVerbatim("CC")<<" Bad vtx fit "<<vtx[iv].ChiDOF<<" wire err "<<vtx[iv].WireErr<<" TimeErr "<<vtx[iv].TimeErr;
2724  // clobber the vertex and references to it
2725  vtx.pop_back();
2726  if(tcl[it1].BeginVtx == iv) tcl[it1].BeginVtx = -99;
2727  if(tcl[it1].EndVtx == iv) tcl[it1].EndVtx = -99;
2728  if(tcl[it2].BeginVtx == iv) tcl[it2].BeginVtx = -99;
2729  if(tcl[it2].EndVtx == iv) tcl[it2].EndVtx = -99;
2730  } // bad fit
2731 
2732  } // ChkVertex()
2733 
2735  bool ClusterCrawlerAlg::ChkSignal(unsigned int iht, unsigned int jht)
2736  {
2737  if(iht > fHits.size() - 1) return false;
2738  if(jht > fHits.size() - 1) return false;
2739  unsigned int wire1 = fHits[iht].WireID().Wire;
2740  float time1 = fHits[iht].PeakTime();
2741  unsigned int wire2 = fHits[jht].WireID().Wire;
2742  float time2 = fHits[jht].PeakTime();
2743  return ChkSignal(wire1, time1, wire2, time2);
2744  } // ChkSignal
2745 
2747  bool ClusterCrawlerAlg::ChkSignal(unsigned int wire1, float time1, unsigned int wire2, float time2)
2748  {
2749  // returns true if there is a signal on the line between
2750  // (wire1, time1) and (wire2, time2).
2751  // Be sure to set time1 < time2 if you are checking for signals on a single wire
2752 
2753  // Gaussian amplitude in bins of size 0.15
2754  const float gausAmp[20] = {1, 0.99, 0.96, 0.90, 0.84, 0.75, 0.67, 0.58, 0.49, 0.40, 0.32, 0.26, 0.20, 0.15, 0.11, 0.08, 0.06, 0.04, 0.03, 0.02};
2755 
2756  // return true if fMinAmp is set ignore wire signal checking in this plane
2757  if(fMinAmp[plane] <= 0) return true;
2758 
2759  // get the begin and end right
2760  unsigned int wireb = wire1;
2761  float timeb = time1;
2762  unsigned int wiree = wire2;
2763  float timee = time2;
2764  // swap them?
2765  if(wiree > wireb) {
2766  wireb = wire2;
2767  timeb = time2;
2768  wiree = wire1;
2769  timee = time1;
2770  }
2771  if(wiree < fFirstWire || wiree > fLastWire) return false;
2772  if(wireb < fFirstWire || wireb > fLastWire) return false;
2773 
2774  int wire0 = wiree;
2775  // checking a single wire?
2776  float slope = 0;
2777  bool oneWire = false;
2778  float prTime, prTimeLo = 0, prTimeHi = 0;
2779  if(wireb == wiree) {
2780  oneWire = true;
2781  if(time1 < time2) {
2782  prTimeLo = time1;
2783  prTimeHi = time2;
2784  } else {
2785  prTimeLo = time2;
2786  prTimeHi = time1;
2787  }
2788  } else {
2789  slope = (timeb - timee) / (wireb - wiree);
2790  }
2791 
2792  int bin;
2793  unsigned short nmissed = 0;
2794  for(unsigned int wire = wiree; wire < wireb + 1; ++wire) {
2795  if(oneWire) {
2796  prTime = (prTimeLo + prTimeHi) / 2;
2797  } else {
2798  prTime = timee + (wire - wire0) * slope;
2799  }
2800  // skip dead wires
2801  if(WireHitRange[wire].first == -1) continue;
2802  // no hits on this wire
2803  if(WireHitRange[wire].first == -2) ++nmissed;
2804  unsigned int firsthit = WireHitRange[wire].first;
2805  unsigned int lasthit = WireHitRange[wire].second;
2806  // if(vtxprt) mf::LogVerbatim("CC")<<" wire "<<wire<<" Hit range "<<firsthit<<" "<<lasthit<<" prTime "<<prTime;
2807  float amp = 0;
2808  for(unsigned int khit = firsthit; khit < lasthit; ++khit) {
2809  // if(vtxprt) mf::LogVerbatim("CC")<<" hit time "<<fHits[khit].PeakTime()<<" rms "<<fHits[khit].RMS()<<" amp "<<fHits[khit].PeakAmplitude()<<" StartTick "<<fHits[khit].StartTick()<<" EndTick "<<fHits[khit].EndTick();
2810  if(oneWire) {
2811  // TODO: This sometimes doesn't work with overlapping hits
2812  // if(prTimeHi > fHits[khit].EndTick()) continue;
2813  // if(prTimeLo < fHits[khit].StartTick()) continue;
2814  // A not totally satisfactory solution
2815  if(prTime < fHits[khit].StartTick()) continue;
2816  if(prTime > fHits[khit].EndTick()) continue;
2817  return true;
2818  } else {
2819  // skip checking if we are far away from prTime on the positive side
2820  if(fHits[khit].PeakTime() - prTime > 500) continue;
2821  bin = std::abs(fHits[khit].PeakTime() - prTime) / fHits[khit].RMS();
2822  bin /= 0.15;
2823  if(bin > 19) continue;
2824  if(bin < 0) continue;
2825  // if(vtxprt) mf::LogVerbatim("CC")<<" bin "<<bin<<" add "<<fHits[khit].PeakAmplitude() * gausAmp[bin]<<" to amp "<<amp;
2826  // add amplitude from all hits
2827  amp += fHits[khit].PeakAmplitude() * gausAmp[bin];
2828  }
2829  } // khit
2830  if(amp < fMinAmp[plane]) ++nmissed;
2831  } // wire
2832  if(nmissed > fAllowNoHitWire) return false;
2833  return true;
2834 
2835  } // ChkSignal()
2836 
2838  bool ClusterCrawlerAlg::SplitCluster(unsigned short icl, unsigned short pos, unsigned short ivx)
2839  {
2840  // split cluster icl into two clusters
2841 
2842  if(tcl[icl].ID < 0) return false;
2843 
2844 // if(pos < 3 || pos > tcl[icl].tclhits.size() - 3) return false;
2845 
2846  // declare icl obsolete
2847  MakeClusterObsolete(icl);
2848 
2849  unsigned short ii, iclnew;
2850  unsigned int iht;
2851 
2852  if(pos > 2) {
2853  // Have enough hits to make a cluster at the Begin end
2854  // Create the first cluster (DS) using the Begin info
2855  clBeginSlp = tcl[icl].BeginSlp;
2856  clBeginSlpErr = tcl[icl].BeginSlpErr;
2857  clBeginAng = tcl[icl].BeginAng;
2858  clBeginWir = tcl[icl].BeginWir;
2859  clBeginTim = tcl[icl].BeginTim;
2860  clBeginChg = tcl[icl].BeginChg;
2861  clStopCode = 5;
2862  clProcCode = tcl[icl].ProcCode;
2863  fcl2hits.clear();
2864  chifits.clear();
2865  hitNear.clear();
2866  chgNear.clear();
2867  for(ii = 0; ii < pos; ++ii) {
2868  iht = tcl[icl].tclhits[ii];
2869  fcl2hits.push_back(iht);
2870  }
2871  // determine the pass in which this cluster was created
2872  pass = tcl[icl].ProcCode - 10 * (tcl[icl].ProcCode / 10);
2873  if(pass > fNumPass-1) pass = fNumPass-1;
2874  // fit the end hits
2875  FitCluster();
2876  clEndSlp = clpar[1];
2877  clEndSlpErr = clparerr[1];
2878  clEndAng = std::atan(fScaleF * clEndSlp);
2879  // find the charge at the end
2880  FitClusterChg();
2881  clEndChg = fAveChg;
2882  if(!TmpStore()) {
2883  RestoreObsoleteCluster(icl);
2884  return false;
2885  }
2886  // associate the End with the supplied vertex
2887  iclnew = tcl.size() - 1;
2888  tcl[iclnew].EndVtx = ivx;
2889  tcl[iclnew].BeginVtx = tcl[icl].BeginVtx;
2890  if(vtxprt) mf::LogVerbatim("CC")<<"SplitCluster made cluster "<<iclnew<<" attached to Begin vtx "<<ivx;
2891  } // pos > 2
2892 
2893  if(pos < tcl[icl].tclhits.size() - 3) {
2894  // have enough hits to make a cluster at the End
2895  // now create the second cluster (US)
2896  clEndSlp = tcl[icl].EndSlp;
2897  clEndSlpErr = tcl[icl].EndSlpErr;
2898  clEndAng = std::atan(fScaleF * clEndSlp);
2899  clEndWir = tcl[icl].EndWir;
2900  clEndTim = tcl[icl].EndTim;
2901  clEndChg = tcl[icl].EndChg;
2902  clStopCode = 5;
2903  clProcCode = tcl[icl].ProcCode;
2904  fcl2hits.clear();
2905  chifits.clear();
2906  hitNear.clear();
2907  chgNear.clear();
2908  bool didFit = false;
2909  for(ii = pos; ii < tcl[icl].tclhits.size(); ++ii) {
2910  iht = tcl[icl].tclhits[ii];
2911  if(inClus[iht] != 0) {
2912  RestoreObsoleteCluster(icl);
2913  return false;
2914  }
2915  fcl2hits.push_back(iht);
2916  // define the Begin parameters
2917  if(fcl2hits.size() == fMaxHitsFit[pass] ||
2918  fcl2hits.size() == fMinHits[pass]) {
2919  FitCluster();
2920  clBeginSlp = clpar[1];
2921  clBeginAng = std::atan(fScaleF * clBeginSlp);
2922  clBeginSlpErr = clparerr[1];
2923  didFit = true;
2924  }
2925  if((unsigned short)fcl2hits.size() == fNHitsAve[pass] + 1) {
2926  FitClusterChg();
2927  clBeginChg = fAveChg;
2928  didFit = true;
2929  }
2930  } // ii
2931  // do a fit using all hits if one wasn't done
2932  if(!didFit) {
2933  FitCluster();
2934  FitClusterChg();
2935  clBeginChg = fAveChg;
2936  }
2937  if(!TmpStore()) {
2938  // clobber the previously stored cluster
2939  MakeClusterObsolete(tcl.size() - 1);
2940  RestoreObsoleteCluster(icl);
2941  return false;
2942  }
2943  // associate the End with the supplied vertex
2944  iclnew = tcl.size() - 1;
2945  tcl[iclnew].BeginVtx = ivx;
2946  tcl[iclnew].EndVtx = tcl[icl].EndVtx;
2947  if(vtxprt) mf::LogVerbatim("CC")<<"SplitCluster made cluster "<<iclnew<<" attached to End vtx "<<ivx;
2948  }
2949 
2950  return true;
2951  } // SplitCluster()
2952 
2954  void ClusterCrawlerAlg::ChkMerge()
2955  {
2956  // Try to merge clusters. Clusters that have been subsumed in other
2957  // clusters, i.e. no longer valid, have ID < 0
2958 
2959  if(tcl.size() < 2) return;
2960  // The size of the ClusterStore vector will increase while merging
2961  // is on-going so the upper limit on it1 is fixed tcl.size() - 1
2962  // before merging starts
2963 
2964  prt = (fDebugPlane == (short)plane && fDebugWire < 0);
2965  if(prt) mf::LogVerbatim("CC")<<"ChkMerge on pass "<<pass;
2966 
2967  unsigned short it1, it2, nh1, pass1, pass2;
2968  float bs1, bth1, bt1, bc1, es1, eth1, et1, ec1;
2969  float bs2, bth2, bt2, bc2, es2, eth2, et2, ec2;
2970  int bw1, ew1, bw2, ew2, ndead;
2971  float dth, dw, angcut, chgrat, chgcut, dtim, timecut, bigslp;
2972  bool bothLong, NoVtx;
2973 
2974  int skipcut, tclsize = tcl.size();
2975  int maxOverlap = 3;
2976 
2977  for(it1 = 0; it1 < tclsize - 1; ++it1) {
2978  // ignore already merged clusters
2979  if(tcl[it1].ID < 0) continue;
2980  if(tcl[it1].CTP != clCTP) continue;
2981  bs1 = tcl[it1].BeginSlp;
2982  // convert slope to angle
2983  bth1 = std::atan(fScaleF * bs1);
2984  // more compact notation for begin/end, wire/time/chg/slp/theta, 1/2
2985  bw1 = tcl[it1].BeginWir;
2986  bt1 = tcl[it1].BeginTim;
2987  bc1 = tcl[it1].BeginChg;
2988  es1 = tcl[it1].EndSlp;
2989  eth1 = tcl[it1].EndAng;
2990  ew1 = tcl[it1].EndWir;
2991  et1 = tcl[it1].EndTim;
2992  ec1 = tcl[it1].EndChg;
2993  nh1 = tcl[it1].tclhits.size();
2994  pass1 = tcl[it1].ProcCode - 10 * (tcl[it1].ProcCode / 10);
2995  if(pass1 > fNumPass) pass1 = fNumPass;
2996  for(it2 = it1 + 1; it2 < tclsize; ++it2) {
2997  // ignore already merged clusters
2998  if(tcl[it1].ID < 0) continue;
2999  if(tcl[it2].ID < 0) continue;
3000  // only merge if they are in the right cryostat/TPC/plane
3001  if(tcl[it2].CTP != clCTP) continue;
3002  // Don't bother if these clusters, if merged, would fail the
3003  // cluster hit fraction cut
3004  if(!ChkMergedClusterHitFrac(it1, it2)) {
3005 // if(prt) mf::LogVerbatim("CC")<<"ID1-2 "<<tcl[it1].ID<<"-"<<tcl[it2].ID<<" fails ChkMergedClusterHitFrac";
3006  continue;
3007  }
3008  bs2 = tcl[it2].BeginSlp;
3009  bth2 = std::atan(fScaleF * bs2);
3010  bw2 = tcl[it2].BeginWir;
3011  bt2 = tcl[it2].BeginTim;
3012  bc2 = tcl[it2].BeginChg;
3013  es2 = tcl[it2].EndSlp;
3014  eth2 = tcl[it2].EndAng;
3015  ew2 = tcl[it2].EndWir;
3016  et2 = tcl[it2].EndTim;
3017  ec2 = tcl[it2].EndChg;
3018  pass2 = tcl[it2].ProcCode - 10 * (tcl[it2].ProcCode / 10);
3019  if(pass2 > fNumPass) pass2 = fNumPass;
3020  // use the more promiscuous pass for cuts
3021  angcut = fKinkAngCut[pass1];
3022  if(fKinkAngCut[pass2] > angcut) angcut = fKinkAngCut[pass2];
3023  skipcut = fMaxWirSkip[pass1];
3024  if(fMaxWirSkip[pass2] > skipcut) skipcut = fMaxWirSkip[pass2];
3025  chgcut = fMergeChgCut[pass1];
3026  if(fMergeChgCut[pass2] > chgcut) chgcut = fMergeChgCut[pass2];
3027 /*
3028  timecut = fTimeDelta[pass];
3029  if(fTimeDelta[pass2] > timecut) timecut = fTimeDelta[pass2];
3030  // increase the time cut for large angle clusters
3031  timecut *= AngleFactor(clpar[1]);
3032 */
3033  // tweak the cuts for long straight tracks
3034  bothLong = (nh1 > 100 && tcl[it2].tclhits.size() > 100);
3035 
3036  // look for US and DS broken clusters w similar angle.
3037  // US cluster 2 merge with DS cluster 1?
3038  // This is the most likely occurrence given the order in which
3039  // clusters are created so put it first.
3040  dth = std::abs(bth2 - eth1);
3041  ndead = DeadWireCount(bw2, ew1);
3042  dw = ew1 - bw2 - ndead;
3043  // require no vertex between
3044  NoVtx = (tcl[it1].EndVtx < 0) && (tcl[it2].BeginVtx < 0);
3045  if(prt && bw2 < (ew1 + maxOverlap) ) mf::LogVerbatim("CC")<<"Chk1 ID1-2 "<<tcl[it1].ID<<"-"<<tcl[it2].ID<<" "<<ew1<<":"<<(int)et1<<" "<<bw2<<":"<<(int)bt2<<" dw "<<dw<<" ndead "<<ndead<<" skipcut "<<skipcut<<" dth "<<dth<<" angcut "<<angcut;
3046  if(NoVtx && bw2 < (ew1 + maxOverlap) && dw < skipcut && dth < angcut) {
3047  chgrat = 2 * fabs(bc2 - ec1) / (bc2 + ec1);
3048  // ignore the charge cut for long tracks with small dth
3049  if(bothLong && dth < 0.05) chgrat = 0.;
3050  // project bw2,bt2 to ew1
3051  dtim = fabs(bt2 + (ew1-bw2)*bs2 - et1);
3052  bigslp = std::abs(bs2);
3053  if(std::abs(es1) > bigslp) bigslp = std::abs(es1);
3054  timecut = fTimeDelta[pass2] * AngleFactor(bigslp);
3055  if(prt) mf::LogVerbatim("CC")<<" dtim "<<dtim<<" timecut "<<(int)timecut<<" ec1 "<<ec1<<" bc2 "<<bc2<<" chgrat "<<chgrat<<" chgcut "<<chgcut<<" es1 "<<es1<<" ChkSignal "<<ChkSignal(ew1, et1, bw2, bt2);
3056  if(chgrat < chgcut && dtim < timecut) {
3057  // ensure there is a signal between cluster ends
3058  if(ChkSignal(ew1, et1, bw2, bt2)) {
3059  DoMerge(it2, it1, 10);
3060  tclsize = tcl.size();
3061  break;
3062  }
3063  } // chgrat < chgcut ...
3064  } // US cluster 2 merge with DS cluster 1?
3065 
3066  // look for US and DS broken clusters w similar angle
3067  // US cluster 1 merge with DS cluster 2?
3068  dth = fabs(bth1 - eth2);
3069  ndead = DeadWireCount(bw1, ew2);
3070  dw = ew2 - bw1 - ndead;
3071  if(prt && bw1 < (ew2 + maxOverlap) && dw < skipcut) mf::LogVerbatim("CC")<<"Chk2 ID1-2 "<<tcl[it1].ID<<"-"<<tcl[it2].ID<<" "<<bw1<<":"<<(int)bt1<<" "<<bw2<<":"<<(int)et2<<" dw "<<dw<<" ndead "<<ndead<<" skipcut "<<skipcut<<" dth "<<dth<<" angcut "<<angcut;
3072  // require no vertex between
3073  NoVtx = (tcl[it2].EndVtx < 0) && (tcl[it1].BeginVtx < 0);
3074  if(NoVtx && bw1 < (ew2 + maxOverlap) && dw < skipcut && dth < angcut ) {
3075  chgrat = 2 * fabs((bc1 - ec2) / (bc1 + ec2));
3076  // ignore the charge cut for long tracks with small dth
3077  if(bothLong && dth < 0.05) chgrat = 0.;
3078  // project sw1,st1 to ew2
3079  dtim = std::abs(bt1 + (ew2-bw1)*bs1 - et2);
3080  bigslp = std::abs(bs1);
3081  if(std::abs(bs2) > bigslp) bigslp = std::abs(bs2);
3082  timecut = fTimeDelta[pass2] * AngleFactor(bigslp);
3083  if(prt) mf::LogVerbatim("CC")<<" dtim "<<dtim<<" err "<<dtim<<" timecut "<<(int)timecut<<" chgrat "<<chgrat<<" chgcut "<<chgcut<<" ChkSignal "<<ChkSignal(bw1, bt1, ew2, et2);
3084  // TODO: we should be checking for a signal here like we did above
3085  if(chgrat < chgcut && dtim < timecut) {
3086  if(ChkSignal(bw1, bt1, ew2, et2)) {
3087  DoMerge(it1, it2, 10);
3088  tclsize = tcl.size();
3089  break;
3090  }
3091  } // chgrat < chgcut ...
3092  } // US cluster 1 merge with DS cluster 2
3093 
3094  if(bw2 < bw1 && ew2 > ew1) {
3095  // look for small cl2 within the wire boundary of cl1
3096  // with similar times and slopes for both clusters
3097  dth = fabs(eth2 - eth1);
3098  dtim = fabs(et1 +(ew2 - ew1 - 1)*es1 - et2);
3099  bigslp = std::abs(es1);
3100  if(std::abs(es1) > bigslp) bigslp = std::abs(es1);
3101  timecut = fTimeDelta[pass2] * AngleFactor(bigslp);
3102  // count the number of wires with no hits on cluster 1
3103  short nmiss1 = bw1 - ew1 + 1 - tcl[it1].tclhits.size();
3104  // compare with the number of hits in cluster 2
3105  short nin2 = tcl[it2].tclhits.size();
3106  if(prt) mf::LogVerbatim("CC")<<"cl2: "<<ew2<<":"<<(int)et2<<" - "<<bw2<<":"<<(int)bt2
3107  <<" within cl1 "<<ew1<<":"<<(int)et1<<" - "<<bw1<<":"<<(int)bt1
3108  <<" ? dth "<<dth<<" dtim "<<dtim<<" nmissed "<<nmiss1<<" timecut "<<timecut<<" FIX THIS CODE";
3109  // make rough cuts before calling ChkMerge12
3110  // this may not work well for long wandering clusters
3111  // TODO fix this code
3112  bool didit = false;
3113  if(dth < 1 && dtim < timecut && nmiss1 >= nin2)
3114  ChkMerge12(it1, it2, didit);
3115  if(didit) {
3116  tclsize = tcl.size();
3117  break;
3118  } //didit
3119  } // small cl2 within the wire boundary of cl1
3120 
3121  if(bw1 < bw2 && ew1 > ew2) {
3122  // look for small cl1 within the wire boundary of cl2
3123  // with similar times and slopes for both clusters
3124  dth = std::abs(eth2 - eth1);
3125  dtim = std::abs(et2 +(ew1 - ew2 - 1)*es2 - et1);
3126  bigslp = std::abs(es1);
3127  if(std::abs(es1) > bigslp) bigslp = std::abs(es1);
3128  timecut = fTimeDelta[pass2] * AngleFactor(bigslp);
3129  // count the number of wires with no hits on cluster 2
3130  short nmiss2 = bw2 - ew2 + 1 - tcl[it2].tclhits.size();
3131  // compare with the number of hits in cluster 1
3132  short nin1 = tcl[it1].tclhits.size();
3133  if(prt) mf::LogVerbatim("CC")<<"cl1: "<<ew1<<":"<<(int)et1<<" - "<<bw1<<":"<<(int)bt1
3134  <<" within cl2 "<<ew2<<":"<<(int)et2<<" - "<<bw2<<":"<<(int)bt2
3135  <<" ? dth "<<dth<<" dtim "<<dtim<<" nmissed "<<nmiss2<<" timecut "<<timecut<<" FIX THIS CODE";
3136  // make rough cuts before calling ChkMerge12
3137  // this may not work well for long wandering clusters
3138  bool didit = false;
3139  if(dth < 1 && dtim < timecut && nmiss2 >= nin1) ChkMerge12(it2, it1, didit);
3140  if(didit) {
3141  tclsize = tcl.size();
3142  break;
3143  } // didit
3144  } // small cl1 within the wire boundary of cl2
3145 
3146  if(tcl[it1].ID < 0) break;
3147  } // cluster 2
3148  if(tcl[it1].ID < 0) continue;
3149  } // cluster 1
3150  }
3151 
3153  void ClusterCrawlerAlg::ChkMerge12(unsigned short it1, unsigned short it2, bool& didit)
3154  {
3155  // Calling routine has done a rough check that cluster it2 is a candidate
3156  // for merging with cluster it1. The wire range spanned by it2 lies
3157  // within the wire range of it1 and the clusters are reasonably close
3158  // together in time.
3159 
3160  // assume that no merging was done
3161  didit = false;
3162 
3163  if(prt) mf::LogVerbatim("CC")<<"ChkMerge12 "<<tcl[it1].ID<<" "<<tcl[it2].ID;
3164 
3165  ClusterStore& cl1 = tcl[it1];
3166  // fill a vector spanning the length of cluster 1 and filled with the hit time
3167  int ew1 = tcl[it1].EndWir;
3168  int bw1 = tcl[it1].BeginWir;
3169  unsigned int iht, hit;
3170  int wire;
3171  std::vector<unsigned int> cl1hits(bw1+1-ew1);
3172 /*
3173  // fill the vector with 0s
3174  for(unsigned int wire = ew1; wire <= bw1; ++wire) {
3175  cl1hits.push_back(0);
3176  }
3177 */
3178  // put in the hit IDs
3179  for(iht = 0; iht < cl1.tclhits.size(); ++iht) {
3180  hit = cl1.tclhits[iht];
3181  wire = fHits[hit].WireID().Wire;
3182  if(wire - ew1 < 0 || wire - ew1 > (int)cl1hits.size()) {
3183 // mf::LogError("CC")<<"ChkMerge12 bad wire "<<(wire-ew1);
3184  return;
3185  }
3186  cl1hits[wire - ew1] = hit;
3187  }
3188  unsigned int ew2 = tcl[it2].EndWir;
3189  float et2 = tcl[it2].EndTim;
3190  // look for the closest wire with a hit on cluster 1
3191  unsigned int wiron1 = 0;
3192  // count the number of missing hits
3193  short nmiss = 0;
3194  for(wire = ew2 - 1; wire > ew1; --wire) {
3195  if(cl1hits[wire - ew1] > 0) {
3196  wiron1 = wire;
3197  break;
3198  }
3199  ++nmiss;
3200  } // wire
3201  if(prt) mf::LogVerbatim("CC")<<"chk next US wire "<<wiron1<<" missed "<<nmiss;
3202  if(wiron1 == 0) return;
3203  if(nmiss > fMaxWirSkip[pass]) return;
3204 
3205  // compare the wires with hits on cluster 2 with the gap in cluster 1
3206  // the number of hit wires that fit in the gap
3207  unsigned int hiton2;
3208  int wiron2;
3209  unsigned short nfit = 0;
3210  for(iht = 0; iht < tcl[it2].tclhits.size(); ++iht) {
3211  hiton2 = tcl[it2].tclhits[iht];
3212  wiron2 = fHits[hiton2].WireID().Wire;
3213  if(wiron2 < ew1 || wiron2 > bw1) return;
3214  if(cl1hits[wiron2 - ew1] == 0) ++nfit;
3215  }
3216  // require complete filling of the gap
3217  if(nfit < tcl[it2].tclhits.size()) return;
3218 
3219  // decode the pass for both clusters and select the matching cuts
3220  unsigned short pass1 = tcl[it1].ProcCode - 10 * (tcl[it1].ProcCode / 10);
3221  unsigned short pass2 = tcl[it2].ProcCode - 10 * (tcl[it2].ProcCode / 10);
3222  unsigned short cpass = pass1;
3223  // use the tighter cuts
3224  if(pass2 < pass1) cpass = pass2;
3225 
3226  // ***** Check End of Cluster 2 matching with middle of cluster 1
3227  if((int) wiron1 - ew1 < 0) return;
3228  unsigned int hiton1 = cl1hits[wiron1 - ew1];
3229  if(hiton1 > fHits.size() - 1) {
3230 // mf::LogError("CC")<<"ChkMerge12 bad hiton1 "<<hiton1;
3231  return;
3232  }
3233  // check the time difference
3234  float timon1 = fHits[hiton1].PeakTime();
3235  float dtim = std::abs(et2 + (wiron1 - ew2) * tcl[it2].EndSlp - timon1);
3236  if(dtim > fTimeDelta[cpass]) return;
3237  // check the slope difference. First do a local fit on cluster 1 near
3238  // the matching point
3239  FitClusterMid(it1, hiton1, 3);
3240  if(clChisq > 20.) return;
3241  // fit parameters are now in clpar.
3242  // check for angle consistency
3243  float dth = std::abs(std::atan(fScaleF * clpar[1]) - std::atan(fScaleF * tcl[it2].EndSlp));
3244  if(prt) mf::LogVerbatim("CC")<<"US dtheta "<<dth<<" cut "<<fKinkAngCut[cpass];
3245  if(dth > fKinkAngCut[cpass]) return;
3246  // make a charge ratio cut. fAveChg was calculated in FitClusterMid
3247  float chgrat = 2 * std::abs(fAveChg - tcl[it2].EndChg) / (fAveChg + tcl[it2].EndChg);
3248  if(prt) mf::LogVerbatim("CC")<<"US chgrat "<<chgrat<<" cut "<<fMergeChgCut[pass];
3249  // ensure that there is a signal on any missing wires at the US end of 1
3250  bool SigOK;
3251  SigOK = ChkSignal(wiron1, timon1, ew2, et2);
3252  if(prt) mf::LogVerbatim("CC")<<"US SigOK? "<<SigOK;
3253  if( !SigOK ) return;
3254 
3255 
3256  // ***** Check Begin of Cluster 2 matching with middle of cluster 1
3257  unsigned int bw2 = tcl[it2].BeginWir;
3258  float bt2 = tcl[it2].BeginTim;
3259  nmiss = 0;
3260  wiron1 = 0;
3261  for(wire = bw2 + 1; wire < bw1; ++wire) {
3262  if(cl1hits[wire - ew1] > 0) {
3263  wiron1 = wire;
3264  break;
3265  }
3266  ++nmiss;
3267  }
3268  if(wiron1 == 0) return;
3269  if(nmiss > fMaxWirSkip[pass]) return;
3270  // fit this section of cluster 1 with 4 hits starting at the hit on the
3271  // closest wire and moving DS
3272  hiton1 = cl1hits[wiron1 - ew1];
3273  if(hiton1 > fHits.size() - 1) {
3274 // mf::LogError("CC")<<"ChkMerge12 bad hiton1 "<<hiton1;
3275  return;
3276  }
3277  timon1 = fHits[hiton1].PeakTime();
3278  dtim = std::abs(bt2 - (wiron1 - bw2) *tcl[it2].BeginSlp - timon1);
3279  if(dtim > fTimeDelta[cpass]) return;
3280  FitClusterMid(it1, hiton1, -3);
3281  if(clChisq > 20.) return;
3282  // check for angle consistency
3283  dth = std::abs(std::atan(fScaleF * clpar[1]) - std::atan(fScaleF * tcl[it2].BeginSlp));
3284  if(prt) mf::LogVerbatim("CC")
3285  <<"DS dtheta "<<dth<<" cut "<<fKinkAngCut[cpass];
3286  if(dth > fKinkAngCut[cpass]) return;
3287  // make a charge ratio cut
3288  chgrat = 2 * std::abs(fAveChg - tcl[it2].BeginChg) / (fAveChg + tcl[it2].BeginChg);
3289  if(prt) mf::LogVerbatim("CC")
3290  <<"DS chgrat "<<chgrat<<" cut "<<fMergeChgCut[pass];
3291  // ensure that there is a signal on any missing wires at the US end of 1
3292  SigOK = ChkSignal(wiron1, timon1, bw2, bt2);
3293  if(prt) mf::LogVerbatim("CC")<<"DS SigOK? "<<SigOK;
3294  if( !SigOK ) return;
3295 
3296  if(prt) mf::LogVerbatim("CC")<<"Merge em";
3297  // success. Merge them
3298  DoMerge(it1, it2, 100);
3299  didit = true;
3300  } // ChkMerge12()
3301 
3303  void ClusterCrawlerAlg::DoMerge(unsigned short it1, unsigned short it2, short inProcCode)
3304  {
3305  // Merge clusters.
3306 
3307  ClusterStore& cl1 = tcl[it1];
3308  ClusterStore& cl2 = tcl[it2];
3309 
3310  if(cl1.tclhits.size() < 3) return;
3311  if(cl2.tclhits.size() < 3) return;
3312 /*
3313  bool myprt = false;
3314  if(plane == 1 && cl1.ID == 84 && cl2.ID == 63) myprt = true;
3315 */
3316 
3317  unsigned int lowire, hiwire, whsize, ii, iht, indx;
3318  // do a fit across the boundary between cl1 and cl2 to
3319  // ensure that they truly should be merged
3320  unsigned int fithit;
3321  // Find the low and high wire for both clusters.
3322  // Assume that cluster 1 is DS
3323  bool cl1DS = true;
3324  hiwire = cl1.BeginWir;
3325  fithit = cl1.tclhits[cl1.tclhits.size() - 2];
3326  if(cl2.BeginWir > hiwire) {
3327  hiwire = cl2.BeginWir;
3328  fithit = cl2.tclhits[cl2.tclhits.size() - 2];
3329  cl1DS = false;
3330  }
3331  lowire = cl1.EndWir;
3332  if(cl2.EndWir < lowire) lowire = cl2.EndWir;
3333 
3334  // make a vector of wire hits
3335  whsize = hiwire + 2 - lowire;
3336  std::vector<int> wirehit(whsize, -1);
3337  // put in the hit IDs for cluster 2
3338  for(ii = 0; ii < cl2.tclhits.size(); ++ii) {
3339  iht = cl2.tclhits[ii];
3340  indx = fHits[iht].WireID().Wire - lowire;
3341  wirehit[indx] = iht;
3342  } // iht
3343  // now cluster 1
3344  for(ii = 0; ii < cl1.tclhits.size(); ++ii) {
3345  iht = cl1.tclhits[ii];
3346  indx = fHits[iht].WireID().Wire - lowire;
3347  wirehit[indx] = iht;
3348 /*
3349  if(myprt) mf::LogVerbatim("CC")
3350  <<"Cl1 hit "<<wire<<":"<<(int)fHits[hit].PeakTime()
3351  <<" wire index "<<index<<" hit index "<<hit;
3352 */
3353  } // iht
3354  // make the new cluster
3355  fcl2hits.clear();
3356  for(std::vector<int>::reverse_iterator rit = wirehit.rbegin(); rit != wirehit.rend(); ++rit) {
3357  if(*rit < 0) continue;
3358  fcl2hits.push_back(*rit);
3359  } // rit
3360 
3361  // fit the 6 hits that are near the merging point
3362  short nhitfit = 6;
3363  FitClusterMid(fcl2hits, fithit, nhitfit);
3364 // std::cout<<"Merge "<<cl1.ID<<" "<<cl2.ID<<" clChisq "<<clChisq<<"\n";
3365  if(clChisq > 5) return;
3366 
3367  // mark cl1 and cl2 obsolete
3368  MakeClusterObsolete(it1);
3369  MakeClusterObsolete(it2);
3370 
3371  short endVtx = 0;
3372  short begVtx = 0;
3373  short del1Vtx = -99;
3374  short del2Vtx = -99;
3375  if(cl1DS) {
3376  // use cluster 1 Begin info
3377  clBeginSlp = cl1.BeginSlp;
3378  clBeginSlpErr = cl1.BeginSlpErr;
3379  clBeginAng = cl1.BeginAng;
3380  clBeginWir = cl1.BeginWir;
3381  clBeginTim = cl1.BeginTim;
3382  clBeginChg = cl1.BeginChg;
3383  clBeginChgNear = cl1.BeginChgNear;
3384  begVtx = cl1.BeginVtx;
3385  del1Vtx = cl1.EndVtx;
3386  // and cluster 2 End info
3387  clEndSlp = cl2.EndSlp;
3388  clEndSlpErr = cl2.EndSlpErr;
3389  clEndAng = cl2.EndAng;
3390  clEndWir = cl2.EndWir;
3391  clEndTim = cl2.EndTim;
3392  clEndChg = cl2.EndChg;
3393  clEndChgNear = cl2.EndChgNear;
3394  endVtx = cl2.EndVtx;
3395  del2Vtx = cl2.BeginVtx;
3396  clStopCode = cl2.StopCode;
3397  } else {
3398  // use cluster 2 Begin info
3399  clBeginSlp = cl2.BeginSlp;
3400  clBeginSlpErr = cl2.BeginSlpErr;
3401  clBeginAng = cl2.BeginAng;
3402  clBeginWir = cl2.BeginWir;
3403  clBeginTim = cl2.BeginTim;
3404  clBeginChg = cl2.BeginChg;
3405  clBeginChgNear = cl2.BeginChgNear;
3406  begVtx = cl2.BeginVtx;
3407  del2Vtx = cl2.EndVtx;
3408  // and cluster 1 End info
3409  clEndSlp = cl1.EndSlp;
3410  clEndSlpErr = cl1.EndSlpErr;
3411  clEndWir = cl1.EndWir;
3412  clEndTim = cl1.EndTim;
3413  clEndChg = cl1.EndChg;
3414  clEndChgNear = cl1.EndChgNear;
3415  endVtx = cl1.EndVtx;
3416  del1Vtx = cl1.BeginVtx;
3417  clStopCode = cl1.StopCode;
3418  }
3419 
3420  // append it to the tcl vector
3421  clCTP = cl1.CTP;
3422  if(!TmpStore()) return;
3423  unsigned short itnew = tcl.size()-1;
3424  if(prt) mf::LogVerbatim("CC")<<"DoMerge "<<cl1.ID<<" "<<cl2.ID<<" -> "<<tcl[itnew].ID;
3425  // stuff the processor code with the current pass
3426  tcl[itnew].ProcCode = inProcCode + pass;
3427  // transfer the vertex info
3428  // delete a vertex between these two?
3429  if(del1Vtx >= 0 && del1Vtx == del2Vtx) vtx[del1Vtx].NClusters = 0;
3430  // preserve the vertex assignments
3431  tcl[itnew].BeginVtx = begVtx;
3432  tcl[itnew].EndVtx = endVtx;
3433  } // DoMerge
3434 
3436  void ClusterCrawlerAlg::PrintVertices()
3437  {
3438 
3439  mf::LogVerbatim myprt("CC");
3440 
3441  if(vtx3.size() > 0) {
3442  // print out 3D vertices
3443  myprt<<"****** 3D vertices ******************************************__2DVtx_Indx__*******\n";
3444  myprt<<"Vtx Cstat TPC Proc X Y Z XEr YEr ZEr pln0 pln1 pln2 Wire\n";
3445  for(unsigned short iv = 0; iv < vtx3.size(); ++iv) {
3446  myprt<<std::right<<std::setw(3)<<std::fixed<<iv<<std::setprecision(1);
3447  myprt<<std::right<<std::setw(7)<<vtx3[iv].CStat;
3448  myprt<<std::right<<std::setw(5)<<vtx3[iv].TPC;
3449  myprt<<std::right<<std::setw(5)<<vtx3[iv].ProcCode;
3450  myprt<<std::right<<std::setw(8)<<vtx3[iv].X;
3451  myprt<<std::right<<std::setw(8)<<vtx3[iv].Y;
3452  myprt<<std::right<<std::setw(8)<<vtx3[iv].Z;
3453  myprt<<std::right<<std::setw(5)<<vtx3[iv].XErr;
3454  myprt<<std::right<<std::setw(5)<<vtx3[iv].YErr;
3455  myprt<<std::right<<std::setw(5)<<vtx3[iv].ZErr;
3456  myprt<<std::right<<std::setw(5)<<vtx3[iv].Ptr2D[0];
3457  myprt<<std::right<<std::setw(5)<<vtx3[iv].Ptr2D[1];
3458  myprt<<std::right<<std::setw(5)<<vtx3[iv].Ptr2D[2];
3459  myprt<<std::right<<std::setw(5)<<vtx3[iv].Wire;
3460  if(vtx3[iv].Wire < 0) {
3461  myprt<<" Matched in all planes";
3462  } else {
3463  myprt<<" Incomplete";
3464  }
3465  myprt<<"\n";
3466  }
3467  } // vtx3.size
3468 
3469  if(vtx.size() > 0) {
3470  // print out 2D vertices
3471  myprt<<"************ 2D vertices ************\n";
3472  myprt<<"Vtx CTP wire error tick error ChiDOF NCl topo cluster IDs\n";
3473  for(unsigned short iv = 0; iv < vtx.size(); ++iv) {
3474  if(fDebugPlane < 3 && fDebugPlane != (int)vtx[iv].CTP) continue;
3475  myprt<<std::right<<std::setw(3)<<std::fixed<<iv<<std::setprecision(1);
3476  myprt<<std::right<<std::setw(6)<<vtx[iv].CTP;
3477  myprt<<std::right<<std::setw(8)<<vtx[iv].Wire<<" +/- ";
3478  myprt<<std::right<<std::setw(4)<<vtx[iv].WireErr;
3479  myprt<<std::right<<std::setw(8)<<vtx[iv].Time<<" +/- ";
3480  myprt<<std::right<<std::setw(4)<<vtx[iv].TimeErr;
3481  myprt<<std::right<<std::setw(8)<<vtx[iv].ChiDOF;
3482  myprt<<std::right<<std::setw(5)<<vtx[iv].NClusters;
3483  myprt<<std::right<<std::setw(6)<<vtx[iv].Topo;
3484  myprt<<" ";
3485  // display the cluster IDs
3486  for(unsigned short ii = 0; ii < tcl.size(); ++ii) {
3487  if(fDebugPlane < 3 && fDebugPlane != (int)tcl[ii].CTP) continue;
3488  if(tcl[ii].ID < 0) continue;
3489  if(tcl[ii].BeginVtx == (short)iv) myprt<<std::right<<std::setw(4)<<tcl[ii].ID;
3490  if(tcl[ii].EndVtx == (short)iv) myprt<<std::right<<std::setw(4)<<tcl[ii].ID;
3491  }
3492  myprt<<"\n";
3493  } // iv
3494  } // vtx.size
3495 
3496  } // PrintVertices
3497 
3500  {
3501 
3502  // prints clusters to the screen for code development
3503  mf::LogVerbatim myprt("CC");
3504 
3505  PrintVertices();
3506 
3507  float aveRMS, aveRes;
3508  myprt<<"*************************************** Clusters *********************************************************************\n";
3509  myprt<<" ID CTP nht Stop Proc beg_W:T bAng bSlp Err bChg end_W:T eAng eSlp Err eChg bVx eVx aveRMS Qual cnt\n";
3510  for(unsigned short ii = 0; ii < tcl.size(); ++ii) {
3511  // print clusters in all planes (fDebugPlane = 3) or in a selected plane
3512  if(fDebugPlane < 3 && fDebugPlane != (int)tcl[ii].CTP) continue;
3513  myprt<<std::right<<std::setw(4)<<tcl[ii].ID;
3514  myprt<<std::right<<std::setw(3)<<tcl[ii].CTP;
3515  myprt<<std::right<<std::setw(5)<<tcl[ii].tclhits.size();
3516  myprt<<std::right<<std::setw(4)<<tcl[ii].StopCode;
3517  myprt<<std::right<<std::setw(6)<<tcl[ii].ProcCode;
3518  unsigned int iTime = tcl[ii].BeginTim;
3519  myprt<<std::right<<std::setw(6)<<tcl[ii].BeginWir<<":"<<iTime;
3520  if(iTime < 10) {
3521  myprt<<" ";
3522  } else if(iTime < 100) {
3523  myprt<<" ";
3524  } else if(iTime < 1000) myprt<<" ";
3525  myprt<<std::right<<std::setw(7)<<std::fixed<<std::setprecision(2)<<tcl[ii].BeginAng;
3526  if(std::abs(tcl[ii].BeginSlp) < 100) {
3527  myprt<<std::right<<std::setw(6)<<std::fixed<<std::setprecision(2)<<tcl[ii].BeginSlp;
3528  } else {
3529  myprt<<std::right<<std::setw(6)<<(int)tcl[ii].BeginSlp;
3530  }
3531  myprt<<std::right<<std::setw(6)<<std::fixed<<std::setprecision(2)<<tcl[ii].BeginSlpErr;
3532  myprt<<std::right<<std::setw(5)<<(int)tcl[ii].BeginChg;
3533 // myprt<<std::right<<std::setw(5)<<std::fixed<<std::setprecision(1)<<tcl[ii].BeginChgNear;
3534  iTime = tcl[ii].EndTim;
3535  myprt<<std::right<<std::setw(6)<<tcl[ii].EndWir<<":"<<iTime;
3536  if(iTime < 10) {
3537  myprt<<" ";
3538  } else if(iTime < 100) {
3539  myprt<<" ";
3540  } else if(iTime < 1000) myprt<<" ";
3541  myprt<<std::right<<std::setw(7)<<std::fixed<<std::setprecision(2)<<tcl[ii].EndAng;
3542  if(std::abs(tcl[ii].EndSlp) < 100) {
3543  myprt<<std::right<<std::setw(6)<<std::fixed<<std::setprecision(2)<<tcl[ii].EndSlp;
3544  } else {
3545  myprt<<std::right<<std::setw(6)<<(int)tcl[ii].EndSlp;
3546  }
3547  myprt<<std::right<<std::setw(6)<<std::fixed<<std::setprecision(2)<<tcl[ii].EndSlpErr;
3548  myprt<<std::right<<std::setw(5)<<(int)tcl[ii].EndChg;
3549 // myprt<<std::right<<std::setw(5)<<std::fixed<<std::setprecision(1)<<tcl[ii].EndChgNear;
3550  myprt<<std::right<<std::setw(5)<<tcl[ii].BeginVtx;
3551  myprt<<std::right<<std::setw(5)<<tcl[ii].EndVtx;
3552  aveRMS = 0;
3553  unsigned int iht = 0;
3554  for(unsigned short jj = 0; jj < tcl[ii].tclhits.size(); ++jj) {
3555  iht = tcl[ii].tclhits[jj];
3556  aveRMS += fHits[iht].RMS();
3557  }
3558  aveRMS /= (float)tcl[ii].tclhits.size();
3559  myprt<<std::right<<std::setw(5)<<std::fixed<<std::setprecision(1)<<aveRMS;
3560  aveRes = 0;
3561  // find cluster tracking resolution
3562  unsigned int hit0, hit1, hit2, cnt = 0;
3563  float arg;
3564  for(unsigned short iht = 1; iht < tcl[ii].tclhits.size()-1; ++iht) {
3565  hit1 = tcl[ii].tclhits[iht];
3566  hit0 = tcl[ii].tclhits[iht-1];
3567  hit2 = tcl[ii].tclhits[iht+1];
3568  // require hits on adjacent wires
3569  if(fHits[hit1].WireID().Wire + 1 != fHits[hit0].WireID().Wire) continue;
3570  if(fHits[hit2].WireID().Wire + 1 != fHits[hit1].WireID().Wire) continue;
3571  arg = (fHits[hit0].PeakTime() + fHits[hit2].PeakTime())/2 - fHits[hit1].PeakTime();
3572  aveRes += arg * arg;
3573  ++cnt;
3574  }
3575  if(cnt > 1) {
3576  aveRes /= (float)cnt;
3577  aveRes = sqrt(aveRes);
3578  // convert to a quality factor
3579  aveRes /= (aveRMS * fHitErrFac);
3580  myprt<<std::right<<std::setw(6)<<std::fixed<<std::setprecision(1)<<aveRes;
3581  myprt<<std::right<<std::setw(5)<<std::fixed<<cnt;
3582  } else {
3583  myprt<<" NA";
3584  myprt<<std::right<<std::setw(5)<<std::fixed<<cnt;
3585  }
3586  myprt<<"\n";
3587  // TEMP
3588 // if(tcl[ii].BeginVtx >= 0) myprt<<"Begin vtx Chi "<<ClusterVertexChi(ii, 0, tcl[ii].BeginVtx)<<"\n";
3589 // if(tcl[ii].EndVtx >= 0) myprt<<"End vtx Chi "<<ClusterVertexChi(ii, 1, tcl[ii].EndVtx)<<"\n";
3590  } // ii
3591 
3592  } // PrintClusters()
3593 
3595  void ClusterCrawlerAlg::TmpGet(unsigned short it1)
3596  {
3597  // copies temp cluster it1 into the fcl2hits vector, etc. This is
3598  // effectively the inverse of cl2TmpStore
3599 
3600  if(it1 > tcl.size()) return;
3601 
3602 
3603  clBeginSlp = tcl[it1].BeginSlp;
3604  clBeginSlpErr = tcl[it1].BeginSlpErr;
3605  clBeginAng = tcl[it1].BeginAng;
3606  clBeginWir = tcl[it1].BeginWir;
3607  clBeginTim = tcl[it1].BeginTim;
3608  clBeginChg = tcl[it1].BeginChg;
3609  clBeginChgNear = tcl[it1].BeginChgNear;
3610  clEndSlp = tcl[it1].EndSlp;
3611  clEndSlpErr = tcl[it1].EndSlpErr;
3612  clEndAng = tcl[it1].EndAng;
3613  clEndWir = tcl[it1].EndWir;
3614  clEndTim = tcl[it1].EndTim;
3615  clEndChg = tcl[it1].EndChg;
3616  clEndChgNear = tcl[it1].EndChgNear;
3617  clStopCode = tcl[it1].StopCode;
3618  clProcCode = tcl[it1].ProcCode;
3619  clCTP = tcl[it1].CTP;
3620  fcl2hits = tcl[it1].tclhits;
3621  }
3622 
3623 
3625  bool ClusterCrawlerAlg::TmpStore()
3626  {
3627 
3628  if(fcl2hits.size() < 2) return false;
3629  if(fcl2hits.size() > fHits.size()) return false;
3630 
3631  if(NClusters == SHRT_MAX) return false;
3632 
3633  ++NClusters;
3634 
3635  unsigned int hit0 = fcl2hits[0];
3636  unsigned int hit;
3637  unsigned int tCST = fHits[hit0].WireID().Cryostat;
3638  unsigned int tTPC = fHits[hit0].WireID().TPC;
3639  unsigned int tPlane = fHits[hit0].WireID().Plane;
3640  unsigned int lastWire = 0;
3641 
3642  for(unsigned short it = 0; it < fcl2hits.size(); ++it) {
3643  hit = fcl2hits[it];
3644  if(inClus[hit] != 0) {
3645 /*
3646  mf::LogError("CC")<<"TmpStore: Trying to use obsolete/used hit "<<hit<<" inClus "<<inClus[hit]
3647  <<" on wire "<<fHits[hit].WireID().Wire<<" on cluster "<<NClusters
3648  <<" in plane "<<plane<<" ProcCode "<<clProcCode;
3649 */
3650  --NClusters;
3651  return false;
3652  }
3653  // check for WireID() consistency
3654  if(fHits[hit].WireID().Cryostat != tCST || fHits[hit].WireID().TPC != tTPC || fHits[hit].WireID().Plane != tPlane) {
3655 /*
3656  mf::LogError("CC")<<"TmpStore: CTP mis-match "<<hit<<" WireID().TPC "<<fHits[hit].WireID().TPC
3657  <<" WireID().Plane "<<fHits[hit].WireID().Plane<<" tCST "<<tCST<<" tTPC "<<tTPC<<" tPlane "<<tPlane
3658  <<" on cluster "<<NClusters<<" ProcCode "<<clProcCode;
3659 */
3660  --NClusters;
3661  return false;
3662  }
3663  // check for decreasing wire number
3664  if(clProcCode < 900 && it > 0 && fHits[hit].WireID().Wire >= lastWire) {
3665 /*
3666  mf::LogError("CC")<<"TmpStore: Hits not in correct wire order. Seed hit "<<fHits[fcl2hits[0]].WireID().Plane<<":"
3667  <<fHits[fcl2hits[0]].WireID().Wire<<":"<<(int)fHits[fcl2hits[0]].PeakTime()<<" it "<<it<<" hit wire "<<fHits[hit].WireID().Wire
3668  <<" ProcCode "<<clProcCode;
3669 */
3670  --NClusters;
3671 /*
3672  for(unsigned short ii = 0; ii < fcl2hits.size(); ++ii) {
3673  mf::LogVerbatim("CC")<<ii<<" "<<fcl2hits[ii]<<" "<<fHits[fcl2hits[ii]].WireID().Plane<<":"<<fHits[fcl2hits[ii]].WireID().Wire<<":"<<(int)fHits[fcl2hits[ii]].PeakTime();
3674  }
3675 */
3676  return false;
3677  }
3678  lastWire = fHits[hit].WireID().Wire;
3679  inClus[hit] = NClusters;
3680  }
3681 
3682  // ensure that the cluster begin/end info is correct
3683 
3684  // define the begin/end charge if it wasn't done already
3685  if(clEndChg <= 0) {
3686  // use the next to the last two hits. The End hit may have low charge
3687  unsigned int ih0 = fcl2hits.size() - 2;
3688  hit = fcl2hits[ih0];
3689  clEndChg = fHits[hit].Integral();
3690  hit = fcl2hits[ih0 - 1];
3691  clEndChg += fHits[hit].Integral();
3692  clEndChg = clEndChg / 2.;
3693  }
3694  if(clBeginChg <= 0) {
3695  // use the 2nd and third hit. The Begin hit may have low charge
3696  hit = fcl2hits[1];
3697  clBeginChg = fHits[hit].Integral();
3698  hit = fcl2hits[2];
3699  clBeginChg += fHits[hit].Integral();
3700  clBeginChg = clBeginChg / 2.;
3701  }
3702 
3703  hit0 = fcl2hits[0];
3704  hit = fcl2hits[fcl2hits.size()-1];
3705 
3706  // store the cluster in the temporary ClusterStore struct
3707  ClusterStore clstr;
3708 
3709  clstr.ID = NClusters;
3710  clstr.BeginSlp = clBeginSlp;
3711  clstr.BeginSlpErr = clBeginSlpErr;
3712  clstr.BeginAng = std::atan(fScaleF * clBeginSlp);
3713  clstr.BeginWir = fHits[hit0].WireID().Wire;
3714  clstr.BeginTim = fHits[hit0].PeakTime();
3715  clstr.BeginChg = clBeginChg;
3716  clstr.BeginChgNear = clBeginChgNear;
3717  clstr.EndSlp = clEndSlp;
3718  clstr.EndSlpErr = clEndSlpErr;
3719  clstr.EndAng = std::atan(fScaleF * clEndSlp);
3720  clstr.EndWir = fHits[hit].WireID().Wire;
3721  clstr.EndTim = fHits[hit].PeakTime();
3722  clstr.EndChg = clEndChg;
3723  clstr.EndChgNear = clEndChgNear;
3724  clstr.StopCode = clStopCode;
3725  clstr.ProcCode = clProcCode;
3726  clstr.BeginVtx = -99;
3727  clstr.EndVtx = -99;
3728  clstr.CTP = EncodeCTP(tCST, tTPC, tPlane);
3729  clstr.tclhits = fcl2hits;
3730  tcl.push_back(clstr);
3731 
3732  return true;
3733  } // TmpStore()
3734 
3736  void ClusterCrawlerAlg::LACrawlUS() {
3737  // Crawl a large angle cluster upstream. Similar to CrawlUS but require
3738  // that a hit be added on each wire
3739 
3740 
3741  unsigned int dhit = fcl2hits[0];
3742  short dwir = fHits[dhit].WireID().Wire;
3743  clLA = true;
3744  prt = false;
3745  if(fDebugPlane == (short)plane && dwir == fDebugWire && fDebugHit > 0)
3746  prt = std::abs(fHits[dhit].PeakTime() - fDebugHit) < 40;
3747 
3748  if(prt) {
3749  mf::LogVerbatim myprt("CC");
3750  myprt<<"******************* LACrawlUS PASS "<<pass<<" Hits ";
3751  for(unsigned short ii = 0; ii < fcl2hits.size(); ++ii) {
3752  unsigned int iht = fcl2hits[fcl2hits.size() - 1 - ii];
3753  myprt<<fHits[iht].WireID().Wire<<":"<<(int)fHits[iht].PeakTime()<<" ";
3754  }
3755  }
3756 
3757  bool SigOK = true;
3758  bool HitOK = true;
3759  short nmissed = 0;
3760  // count the number of kinks encountered. Hits US of the kink are removed
3761  // and crawling continues unless another kink is encountered
3762  unsigned short kinkOnWire = 0;
3763  unsigned int it = fcl2hits.size() - 1;
3764  unsigned int lasthit = fcl2hits[it];
3765  unsigned int lastwire = fHits[lasthit].WireID().Wire;
3766  unsigned int prevHit, prevWire;
3767  bool ChkCharge = false;
3768  for(unsigned int nextwire = lastwire-1; nextwire >= fFirstWire; --nextwire) {
3769  if(prt) mf::LogVerbatim("CC")<<"LACrawlUS: next wire "<<nextwire<<" HitRange "<<WireHitRange[nextwire].first;
3770  // stop crawling if there is a nearby vertex
3771  if(CrawlVtxChk(nextwire)) {
3772  if(prt) mf::LogVerbatim("CC")<<"LACrawlUS: stop at vertex";
3773  clStopCode = 6;
3774  break;
3775  }
3776  // AddLAHit will merge the hit on nextwire if necessary
3777  AddLAHit(nextwire, ChkCharge, HitOK, SigOK);
3778  if(prt) mf::LogVerbatim("CC")<<"LACrawlUS: HitOK "<<HitOK<<" SigOK "<<SigOK<<" nmissed SigOK "<<nmissed<<" cut "<<fAllowNoHitWire;
3779  if(SigOK) nmissed = 0;
3780  if(!SigOK) {
3781  ++nmissed;
3782  if(nmissed > fAllowNoHitWire) {
3783  clStopCode = 1;
3784  break;
3785  }
3786  continue;
3787  }
3788  // If a hit was added after a gap, check to see if there is indeed
3789  // a wire signal in the gap
3790  if(HitOK) {
3791  prevHit = fcl2hits[fcl2hits.size() - 2];
3792  prevWire = fHits[prevHit].WireID().Wire;
3793  if(prevWire > nextwire + 2) {
3794  if(!ChkSignal(fcl2hits[fcl2hits.size()-1], fcl2hits[fcl2hits.size()-2])) {
3795  // no hit so trim the last hit and quit
3796  FclTrimUS(1);
3797  break;
3798  } // no signal
3799  } // prevWire > nextwire + 2
3800  } // HitOK
3801  // Merge all of the hit multiplets in the fcl2hits array into single
3802  // hits when enough hits have been found to call this a credible large
3803  // angle cluster. The last hit was already merged in AddHit
3804  if(fcl2hits.size() == 4) {
3805  bool didMerge;
3806  for(unsigned short kk = 0; kk< fcl2hits.size()-1; ++kk) {
3807  unsigned int hit = fcl2hits[kk];
3808  if(mergeAvailable[hit]) MergeHits(hit, didMerge);
3809  }
3810  // update the fit
3811  FitCluster();
3812  clBeginSlp = clpar[1];
3813  // start checking the charge ratio when adding new hits
3814  ChkCharge = true;
3815  continue;
3816  } // fcl2hits.size() == 4
3817  unsigned short chsiz = chifits.size()-1;
3818  // chsiz is fcl2hits.size() - 1...
3819  if(chsiz < 6) continue;
3820  if(fKinkChiRat[pass] <= 0) continue;
3821  if(chifits.size() != fcl2hits.size()) {
3822  mf::LogError("CC")
3823  <<"LACrawlUS: chifits size error "<<chifits.size()<<" "<<fcl2hits.size();
3824  return;
3825  }
3826  if(prt) mf::LogVerbatim("CC") <<"Kink chk "<<chifits[chsiz]<<" "<<chifits[chsiz-1]
3827  <<" "<<chifits[chsiz-2]<<" "<<chifits[chsiz-3];
3828  if( chifits[chsiz-1] > fKinkChiRat[pass] * chifits[chsiz-2] &&
3829  chifits[chsiz] > fKinkChiRat[pass] * chifits[chsiz-1]) {
3830  // find the kink angle (crudely) from the 0th and 2nd hit
3831  unsigned int ih0 = fcl2hits.size() - 1;
3832  unsigned int hit0 = fcl2hits[ih0];
3833  unsigned int ih2 = ih0 - 2;
3834  unsigned int hit2 = fcl2hits[ih2];
3835  float dt02 = fHits[hit2].PeakTime() - fHits[hit0].PeakTime();
3836  float dw02 = fHits[hit2].WireID().Wire - fHits[hit0].WireID().Wire;
3837  float th02 = std::atan( fScaleF * dt02 / dw02);
3838  // and the 3rd and 5th hit
3839  unsigned int ih3 = ih2 - 1;
3840  unsigned int hit3 = fcl2hits[ih3];
3841  unsigned int ih5 = ih3 - 2;
3842  unsigned int hit5 = fcl2hits[ih5];
3843  float dt35 = fHits[hit5].PeakTime() - fHits[hit3].PeakTime();
3844  float dw35 = fHits[hit5].WireID().Wire - fHits[hit3].WireID().Wire;
3845  float th35 = std::atan(fScaleF * dt35 / dw35);
3846  float dth = std::abs(th02 - th35);
3847  if(prt) mf::LogVerbatim("CC")<<" Kink angle "<<std::setprecision(3)<<dth<<" cut "<<fKinkAngCut[pass];
3848  if(dth > fKinkAngCut[pass]) {
3849  // hit a kink. Lop of the first 3 hits, refit and keep crawling?
3850  FclTrimUS(3);
3851  FitCluster();
3852  // See if this is a second kink and it is close to the first
3853  // kink (which had hits removed).
3854  if(kinkOnWire > 0) {
3855  if(kinkOnWire - nextwire < 4) {
3856  if(prt) mf::LogVerbatim("CC") <<"Hit a second kink. kinkOnWire = "<<kinkOnWire<<" Stopping";
3857  // set the kink stop code
3858  clStopCode = 3;
3859  break;
3860  }
3861  }
3862  kinkOnWire = nextwire;
3863  if(prt) mf::LogVerbatim("CC")<<"Removed kink hits";
3864  } // kinkang check
3865  } // chifits test
3866  } // nextwire
3867 
3868  CheckClusterHitFrac(prt);
3869 
3870  clProcCode += 300;
3871  if(prt) mf::LogVerbatim("CC")<<"LACrawlUS done. Nhits = "<<fcl2hits.size();
3872  prt = false;
3873  } // LACrawlUS
3874 
3876  void ClusterCrawlerAlg::CrawlUS()
3877  {
3878  // Crawl along a trail of hits moving upstream
3879 
3880  if(fcl2hits.size() < 2) return;
3881 
3882  unsigned int dhit = fcl2hits[0];
3883  int dwir = fHits[dhit].WireID().Wire;
3884  clLA = false;
3885 
3886  prt = false;
3887  if(fDebugPlane == (short)plane && dwir == fDebugWire && fDebugHit > 0)
3888  prt = std::abs(fHits[dhit].PeakTime() - fDebugHit) < 20;
3889 
3890  if(prt) {
3891  mf::LogVerbatim myprt("CC");
3892  myprt<<"******************* Start CrawlUS on pass "<<pass<<" Hits: ";
3893  for(unsigned short ii = 0; ii < fcl2hits.size(); ++ii) {
3894  unsigned int iht = fcl2hits[fcl2hits.size() - 1 - ii];
3895  myprt<<fHits[iht].WireID().Wire<<":"<<(int)fHits[iht].PeakTime()<<" ";
3896  }
3897  myprt<<"\n";
3898  }
3899 
3900  // SigOK = true if there is a ADC signal near the projected cluster position
3901  bool SigOK = true;
3902  bool HitOK = true;
3903  // count the number of missed hits on adjacent wires
3904  short nmissed = 0;
3905  // count the number of added hits after skipping
3906  short nHitAfterSkip = 0;
3907  bool DidaSkip = false;
3908  bool PostSkip = false;
3909  unsigned int it = fcl2hits.size() - 1;
3910  unsigned int lasthit = fcl2hits[it];
3911  if(lasthit > fHits.size() - 1) {
3912  mf::LogError("CC")<<"CrawlUS bad lasthit "<<lasthit;
3913  return;
3914  }
3915  unsigned int lastwire = fHits[lasthit].WireID().Wire;
3916  if(prt) mf::LogVerbatim("CC")<<"CrawlUS: last wire "<<lastwire<<" hit "<<lasthit;
3917 
3918  unsigned int lastWireWithSignal = lastwire;
3919  unsigned short nDroppedHit = 0;
3920 
3921  for(unsigned int nextwire = lastwire-1; nextwire > fFirstWire; --nextwire) {
3922  if(prt) mf::LogVerbatim("CC")<<"CrawlUS: next wire "<<nextwire<<" HitRange "<<WireHitRange[nextwire].first;
3923  // stop crawling if there is a nearby vertex
3924  if(CrawlVtxChk(nextwire)) {
3925  if(prt) mf::LogVerbatim("CC")<<"CrawlUS: stop at vertex";
3926  clStopCode = 6;
3927  break;
3928  }
3929  // Switch to large angle crawling?
3930  if(std::abs(clpar[1]) > fLAClusSlopeCut) {
3931  if(prt) mf::LogVerbatim("CC")<<">>>>> CrawlUS: Switching to LACrawlUS";
3932  LACrawlUS();
3933  return;
3934  }
3935  // add hits and check for PH and width consistency
3936  AddHit(nextwire, HitOK, SigOK);
3937  if(prt) mf::LogVerbatim("CC")<<"CrawlUS: HitOK "<<HitOK<<" SigOK "<<SigOK<<" nmissed "<<nmissed;
3938  if(SigOK) lastWireWithSignal = nextwire;
3939  if(!HitOK) {
3940  // no hit on this wire. Was there a signal or dead wire?
3941  if(SigOK) {
3942  // no hit on the wire but there is a signal
3943  ++nmissed;
3944 /*
3945  // stop if too many missed wires
3946  if(prt) mf::LogVerbatim("CC")<<" nmissed "<<nmissed<<" cut "<<fMaxWirSkip[pass];
3947  if(nmissed > fMaxWirSkip[pass]) {
3948  clStopCode = 1;
3949  break;
3950  }
3951 */
3952  // see if we are in the PostSkip phase and missed more than 1 wire
3953  if(PostSkip && nmissed > fMinWirAfterSkip[pass]) {
3954  // cluster is really short
3955  if((int)(fcl2hits.size() - nHitAfterSkip) < 4) {
3956 // RestoreUnMergedClusterHits(-1);
3957  fcl2hits.clear();
3958  return;
3959  }
3960  if(prt) mf::LogVerbatim("CC")<<" PostSkip && nmissed = "<<nmissed;
3961  clStopCode = 2;
3962  FclTrimUS(nHitAfterSkip);
3963  FitCluster();
3964  if(clChisq > 90.) {
3965 // RestoreUnMergedClusterHits(-1);
3966  fcl2hits.clear();
3967  return;
3968  }
3969  FitCluster();
3970  return;
3971  } // PostSkip && nmissed >
3972  if(nmissed > 1) {
3973  DidaSkip = true;
3974  PostSkip = false;
3975  }
3976  } // SigOK
3977  else {
3978  // SigOK is false
3979  clStopCode = 0;
3980  lasthit = fcl2hits[fcl2hits.size() - 1];
3981  if((lastWireWithSignal - nextwire) > fAllowNoHitWire) {
3982  if(prt) mf::LogVerbatim("CC")<<"No hit or signal on wire "<<nextwire<<" last wire with signal "<<lastWireWithSignal<<" exceeding fAllowNoHitWire "<<fAllowNoHitWire<<" Break!";
3983  break;
3984  }
3985  } // else SigOK false
3986  } // !HitOK
3987  else {
3988  if(prt) mf::LogVerbatim("CC")<<" CrawlUS check clChisq "<<clChisq<<" cut "<<fChiCut[pass];
3989  if(clChisq > fChiCut[pass]) {
3990  if(fcl2hits.size() < 3) {
3991  fcl2hits.clear();
3992  return;
3993  }
3994  // a fit error occurred. Lop off the leading hit and refit
3995  if(prt) mf::LogVerbatim("CC")<<"ADD- Bad clChisq "<<clChisq<<" dropping hit";
3996  FclTrimUS(1);
3997  FitCluster();
3998  ++nDroppedHit;
3999  if(nDroppedHit > 4) {
4000  if(prt) mf::LogVerbatim("CC")<<" Too many dropped hits: "<<nDroppedHit<<" Stop crawling";
4001  break;
4002  } // too many dropped hits
4003  if(clChisq > fChiCut[pass]) {
4004  if(prt) mf::LogVerbatim("CC")<<" Bad clChisq "<<clChisq<<" after dropping hit. Stop crawling";
4005  break;
4006  }
4007  FitClusterChg();
4008 /*
4009  // bail out if we have had repeated failures
4010  lasthit = fcl2hits[fcl2hits.size() - 1];
4011  if((fHits[lasthit].WireID().Wire - nextwire) > fMinWirAfterSkip[pass]) {
4012  if(prt) mf::LogVerbatim("CC")<<" Too many missed wires "<<(fHits[lasthit].WireID().Wire - nextwire)<<" cut "<<fMinWirAfterSkip[pass];
4013  break;
4014  }
4015 */
4016  continue;
4017  } // clChisq > fChiCut[0]
4018  // monitor the onset of a kink. Look for a progressive increase
4019  // in chisq for the previous 0 - 2 hits.
4020  if(chifits.size() > 5 && fKinkChiRat[pass] > 0) {
4021  if(chifits.size() != fcl2hits.size()) {
4022  mf::LogError("CC")<<"CrawlUS: chifits size error "<<chifits.size()<<" "<<fcl2hits.size();
4023  return;
4024  }
4025  unsigned short chsiz = chifits.size()-1;
4026  if(prt) mf::LogVerbatim("CC")<<"Kink chk "<<chifits[chsiz]<<" "<<chifits[chsiz-1]<<" "<<chifits[chsiz-2]<<" "<<chifits[chsiz-3];
4027  if( chifits[chsiz-1] > fKinkChiRat[pass] * chifits[chsiz-2] &&
4028  chifits[chsiz] > fKinkChiRat[pass] * chifits[chsiz-1]) {
4029  if(fcl2hits.size() != chifits.size()) {
4030  mf::LogError("CC")<<"bad kink check size "<<chifits.size()<<" "<<fcl2hits.size()<<" plane "<<plane<<" cluster "<<dwir<<":"<<dhit;
4031  continue;
4032  }
4033  if(EndKinkAngle() > fKinkAngCut[pass]) {
4034  if(prt) mf::LogVerbatim("CC")<<"******************* Stopped tracking - kink angle "<<EndKinkAngle()
4035  <<" > "<<fKinkAngCut[pass]<<" Removing 3 hits";
4036  // kill the last 3 hits and refit
4037  FclTrimUS(3);
4038  FitCluster();
4039  FitClusterChg();
4040  // set the kink stop code and quit
4041 // clStopCode = 3;
4042 // break;
4043  } // kinkang check
4044  } // chifits check
4045  } // chifits.size() > 5
4046  // done with kink check
4047  // update the cluster Begin information?
4048  if(fcl2hits.size() == fMaxHitsFit[pass] ||
4049  fcl2hits.size() == fMinHits[pass]) {
4050  clBeginSlp = clpar[1];
4051  clBeginSlpErr = clparerr[1];
4052  }
4053  // define the begin cluster charge if it's not defined yet
4054  if(clBeginChg <= 0 && fAveChg > 0) {
4055  clBeginChg = fAveChg;
4056  if(prt) mf::LogVerbatim("CC")<<" Set clBeginChg "<<clBeginChg;
4057  }
4058  // reset nmissed
4059  nmissed = 0;
4060  // start counting hits added after skipping
4061  if(DidaSkip) {
4062  // start PostSkip phase
4063  PostSkip = true;
4064  DidaSkip = false;
4065  nHitAfterSkip = 0;
4066  } // DidaSkip
4067  // check for PostSkip phase
4068  if(PostSkip) {
4069  // end the PostSkip phase if there are enough hits
4070  ++nHitAfterSkip;
4071  if(nHitAfterSkip == fMinWirAfterSkip[pass]) PostSkip = false;
4072  }
4073  // check for bad chisq
4074  if(clChisq > fChiCut[pass]) {
4075  if(prt) mf::LogVerbatim("CC")<<"<<ADD- Bad chisq "<<clChisq;
4076  // remove the last few hits if there is a systematic increase in chisq and re-fit
4077  float chirat;
4078  unsigned short lopped = 0;
4079  for(unsigned short nlop = 0; nlop < 4; ++nlop) {
4080  unsigned short cfsize = chifits.size() - 1;
4081  chirat = chifits[cfsize] / chifits[cfsize - 1];
4082  if(prt) mf::LogVerbatim("CC")<<"chirat "<<chirat<<" last hit "<<fcl2hits[fcl2hits.size()-1];
4083  if(chirat < 1.2) break;
4084  if(prt) mf::LogVerbatim("CC")<<"<<ADD- Bad chisq. Bad chirat "<<chirat;
4085  FclTrimUS(1);
4086  ++lopped;
4087  if(fcl2hits.size() < 6) break;
4088  if(chifits.size() < 6) break;
4089  } // nlop
4090  if(fcl2hits.size() < 6) {
4091  clStopCode = 4;
4092  if(prt) mf::LogVerbatim("CC")<<"Bad fit chisq - short cluster. Break";
4093  break;
4094  }
4095  if(lopped == 0 && fcl2hits.size() > 5) {
4096  if(prt) mf::LogVerbatim("CC")<<"<<ADD- Bad chisq. remove 1 hit";
4097  FclTrimUS(1);
4098  ++lopped;
4099  }
4100  FitCluster();
4101  FitClusterChg();
4102  if(prt) mf::LogVerbatim("CC")<<"Bad fit chisq - removed "<<lopped<<" hits. Continue";
4103  } // clChisq > fChiCut[pass]
4104  } // !HitOK check
4105  } // nextwire
4106  if(prt) mf::LogVerbatim("CC")<<"******************* CrawlUS normal stop. size "<<fcl2hits.size();
4107 
4108  bool reFit = false;
4109  // end kink angle check
4110  if(fcl2hits.size() > 5) {
4111  // check for a kink at the US end
4112  if(prt) mf::LogVerbatim("CC")<<"EndKinkAngle check "<<EndKinkAngle()<<" cut "<<fKinkAngCut[pass];
4113  if(EndKinkAngle() > fKinkAngCut[pass]) {
4114  if(prt) mf::LogVerbatim("CC")<<"EndKinkAngle removes 3 hits ";
4115  FclTrimUS(3);
4116  reFit = true;
4117  }
4118  } // fcl2hits.size() > 5
4119 
4120  // count the number of hits on adjacent wires at the leading edge and
4121  // ensure that the count is consistent with fMinWirAfterSkip
4122  if((unsigned short)fcl2hits.size() > fMinWirAfterSkip[pass] + 3) {
4123  unsigned int ih0 = fcl2hits.size() - 1;
4124  unsigned int hit0 = fcl2hits[ih0];
4125  unsigned int uswir = fHits[hit0].WireID().Wire;
4126  unsigned int nxtwir;
4127  unsigned short nAdjHit = 0;
4128  for(unsigned short ii = ih0 - 1; ii > 0; --ii) {
4129  nxtwir = fHits[ fcl2hits[ii] ].WireID().Wire;
4130  ++nAdjHit;
4131  if(nxtwir != uswir + 1) break;
4132  // break if there are enough hits
4133  if( nAdjHit == fMinWirAfterSkip[pass] ) break;
4134  uswir = nxtwir;
4135  } // ii
4136  // lop off hits?
4137  if(nAdjHit < fMinWirAfterSkip[pass]) {
4138  if(prt) mf::LogVerbatim("CC")<<"fMinWirAfterSkip removes "<<nAdjHit<<" hits ";
4139  FclTrimUS(nAdjHit);
4140  reFit = true;
4141  }
4142  } // fcl2hits.size() > fMinWirAfterSkip[pass] + 3
4143 
4144  // check for a bad hit on the end
4145  if(!reFit && fcl2hits.size() > 3) {
4146  float chirat = chifits[chifits.size()-1] / chifits[chifits.size()-2];
4147  if(prt) mf::LogVerbatim("CC")<<"Last hit chirat "<<chirat<<" cut "<<fKinkChiRat[pass];
4148  if(prt) mf::LogVerbatim("CC")<<"Check "<<clChisq<<" "<<chifits[chifits.size()-1]<<" "<<chifits[chifits.size()-2];
4149  if(chirat > fKinkChiRat[pass]) {
4150  if(prt) mf::LogVerbatim("CC")<<"<<ADD- at end";
4151  FclTrimUS(1);
4152  reFit = true;
4153  }
4154  } // !reFit
4155 
4156  if(reFit) {
4157  FitCluster();
4158  FitClusterChg();
4159  }
4160  CheckClusterHitFrac(prt);
4161  if(prt) mf::LogVerbatim("CC")<<"******************* CrawlUS done. Size "
4162  <<fcl2hits.size()<<" min length for this pass "<<fMinHits[pass];
4163 
4164  prt = false;
4165  } // CrawlUS()
4166 
4168  float ClusterCrawlerAlg::EndKinkAngle()
4169  {
4170  // find the kink angle (crudely) from the 0th and 2nd hit on the cluster under construction
4171 
4172  unsigned int ih0 = fcl2hits.size() - 1;
4173  unsigned int hit0 = fcl2hits[ih0];
4174  unsigned int ih2 = ih0 - 2;
4175  unsigned int hit2 = fcl2hits[ih2];
4176  float dt02 = fHits[hit2].PeakTime() - fHits[hit0].PeakTime();
4177  float dw02 = fHits[hit2].WireID().Wire - fHits[hit0].WireID().Wire;
4178  float th02 = std::atan( fScaleF * dt02 / dw02);
4179  // and the 3rd and 5th hit
4180  unsigned int ih3 = ih2 - 1;
4181  unsigned int hit3 = fcl2hits[ih3];
4182  unsigned int ih5 = ih3 - 2;
4183  unsigned int hit5 = fcl2hits[ih5];
4184  float dt35 = fHits[hit5].PeakTime() - fHits[hit3].PeakTime();
4185  float dw35 = fHits[hit5].WireID().Wire - fHits[hit3].WireID().Wire;
4186  float th35 = std::atan(fScaleF * dt35 / dw35);
4187  return std::abs(th02 - th35);
4188  }
4189 
4191  bool ClusterCrawlerAlg::ChkMergedClusterHitFrac(unsigned short it1, unsigned short it2)
4192  {
4193  // This routine is called before two tcl clusters, it1 and it2, are slated to be
4194  // merged to see if they will satisfy the minimum hit fraction criterion
4195  if(it1 > tcl.size()-1) return false;
4196  if(it2 > tcl.size()-1) return false;
4197  unsigned int eWire = 99999;
4198  unsigned int bWire = 0, wire;
4199  if(tcl[it1].BeginWir > bWire) bWire = tcl[it1].BeginWir;
4200  if(tcl[it2].BeginWir > bWire) bWire = tcl[it2].BeginWir;
4201  if(tcl[it1].EndWir < eWire) eWire = tcl[it1].EndWir;
4202  if(tcl[it2].EndWir < eWire) eWire = tcl[it2].EndWir;
4203  unsigned int mergedClusterLength = bWire - eWire + 1;
4204  // define a vector of size = length of the wire range
4205  std::vector<bool> cHits(mergedClusterLength, false);
4206  // set the elements true if there is a hit
4207  unsigned int ii, iht, indx;
4208  for(ii = 0; ii < tcl[it1].tclhits.size(); ++ii) {
4209  iht = tcl[it1].tclhits[ii];
4210  if(iht > fHits.size()-1) {
4211  mf::LogError("CC")<<"ChkMergedClusterHitFrac bad iht ";
4212  return false;
4213  }
4214  indx = fHits[iht].WireID().Wire - eWire;
4215  cHits[indx] = true;
4216  } // ii
4217  for(ii = 0; ii < tcl[it2].tclhits.size(); ++ii) {
4218  iht = tcl[it2].tclhits[ii];
4219  if(iht > fHits.size()-1) {
4220  mf::LogError("CC")<<"ChkMergedClusterHitFrac bad iht ";
4221  return false;
4222  }
4223  indx = fHits[iht].WireID().Wire - eWire;
4224  cHits[indx] = true;
4225  } // ii
4226  // set cHits true if the wire is dead
4227  for(ii = 0; ii < cHits.size(); ++ii) {
4228  wire = eWire + ii;
4229  if(WireHitRange[wire].first == -1) cHits[ii] = true;
4230  }
4231  // count the number of wires with hits
4232  float nhits = std::count(cHits.begin(), cHits.end(), true);
4233  float hitFrac = nhits / (float)cHits.size();
4234  return (hitFrac > fMinHitFrac);
4235  } // ChkMergedClusterHitFrac
4236 
4238  void ClusterCrawlerAlg::CheckClusterHitFrac(bool prt)
4239  {
4240 
4241 
4242  // Find the fraction of the wires on the cluster that have
4243  // hits.
4244  unsigned int iht = fcl2hits[fcl2hits.size() - 1];
4245  clEndWir = fHits[iht].WireID().Wire;
4246  clBeginWir = fHits[fcl2hits[0]].WireID().Wire;
4247  float hitFrac = (float)(fcl2hits.size() + DeadWireCount()) / (float)(clBeginWir - clEndWir + 1);
4248 
4249  if(hitFrac < fMinHitFrac) {
4250 // RestoreUnMergedClusterHits(-1);
4251  if(prt) mf::LogVerbatim("CC")<<"CheckClusterHitFrac: Poor hit fraction "<<hitFrac<<" clBeginWir "<<clBeginWir
4252  <<" clEndWir "<<clEndWir<<" size "<<fcl2hits.size()<<" DeadWireCount "<<DeadWireCount();
4253  fcl2hits.clear();
4254  return;
4255  } // hitFrac
4256 
4257 /* TODO: Does this make sense?
4258  // lop off the last hit if it is part of a hit multiplet
4259  if(fHits[iht].Multiplicity() > 1) {
4260  fcl2hits.resize(fcl2hits.size() - 1);
4261  }
4262 */
4263  // check for short track ghosts
4264  if(fcl2hits.size() < 5) {
4265  unsigned short nsing = 0;
4266  for(unsigned short iht = 0; iht < fcl2hits.size(); ++iht) if(fHits[fcl2hits[iht]].Multiplicity() == 1) ++nsing;
4267  hitFrac = (float)nsing / (float)fcl2hits.size();
4268  if(hitFrac < fMinHitFrac) {
4269 // RestoreUnMergedClusterHits(-1);
4270  fcl2hits.clear();
4271  if(prt) mf::LogVerbatim("CC")<<"CheckClusterHitFrac: Poor short track hit fraction "<<hitFrac;
4272  return;
4273  } // hitFrac
4274  } // short ghost track check
4275 
4276  // analyze the pattern of nearby charge
4277  // will need the cluster charge so calculate it here if it isn't defined yet
4278  if(clBeginChg <= 0) {
4279  unsigned int iht, nht = 0;
4280  for(unsigned short ii = 0; ii < fcl2hits.size(); ++ii) {
4281  iht = fcl2hits[ii];
4282  clBeginChg += fHits[iht].Integral();
4283  ++nht;
4284  if(nht == 8) break;
4285  }
4286  clBeginChg /= (float)nht;
4287  } // clBeginChg == 0
4288  // handle short vs long clusters
4289  unsigned short cnt = chgNear.size()/2;
4290  // get the average charge from <= 30 hits at each end
4291  if(chgNear.size() > 60) cnt = 30;
4292  clBeginChgNear = 0;
4293  clEndChgNear = 0;
4294  for(unsigned short ids = 0; ids < cnt; ++ids) {
4295  clBeginChgNear += chgNear[ids];
4296  clEndChgNear += chgNear[chgNear.size() - 1 - ids];
4297  }
4298  clBeginChgNear /= (float)cnt;
4299  clEndChgNear /= (float)cnt;
4300 
4301  } // CheckClusterHitFrac()
4302 
4304  void ClusterCrawlerAlg::FitClusterMid(unsigned short it1, unsigned int ihtin, short nhit)
4305  {
4306  FitClusterMid(tcl[it1].tclhits, ihtin, nhit);
4307  } // FitClusterMid
4308 
4310  void ClusterCrawlerAlg::FitClusterMid(std::vector<unsigned int>& hitVec, unsigned int ihtin, short nhit)
4311  {
4312  // Fits hits on temp cluster it1 to a line starting at hit ihtin and including
4313  // nhit hits incrementing towards the hit vector End when nhit > 0 and
4314  // decrementing towards the hit vector Begin when nhit < 0.
4315  // The fit params are stashed in the clpar and clparerr arrays.
4316  // fAveChg is re-calculated as well.
4317 
4318 
4319  // set chisq bad in case something doesn't work out
4320  clChisq = 99.;
4321 
4322  if(hitVec.size() < 3) return;
4323 
4324  std::vector<float> xwir;
4325  std::vector<float> ytim;
4326  std::vector<float> ytimerr2;
4327 
4328  unsigned short ii, hitcnt = 0, nht = 0, usnhit;
4329  float wire0 = 0;
4330  unsigned int iht;
4331  bool UseEm = false;
4332  fAveChg = 0.;
4333  fChgSlp = 0.;
4334 
4335  if(nhit > 0) {
4336  usnhit = nhit;
4337  // find the first desired hit and move towards the End
4338  for(ii = 0; ii < hitVec.size(); ++ii) {
4339  iht = hitVec[ii];
4340  if(iht > fHits.size()-1) {
4341  mf::LogError("CC")<<"FitClusterMid bad iht "<<iht;
4342  return;
4343  }
4344  // look for the desired first hit. Use this as the origin wire
4345  if(iht == ihtin) {
4346  UseEm = true;
4347  wire0 = fHits[iht].WireID().Wire;
4348  }
4349  // use hits after finding the first desired hit
4350  if(UseEm) {
4351  xwir.push_back((float)fHits[iht].WireID().Wire - wire0);
4352  ytim.push_back(fHits[iht].PeakTime());
4353  // pass the error^2 to the fitter
4354  float terr = fHitErrFac * fHits[iht].RMS();
4355  ytimerr2.push_back(terr * terr);
4356  fAveChg += fHits[iht].Integral();
4357  ++hitcnt;
4358  if(hitcnt == usnhit) break;
4359  }
4360  }
4361  nht = hitcnt;
4362  } else {
4363  usnhit = -nhit;
4364  // find the first desired hit and move towards the Begin
4365  for(auto ii = hitVec.crbegin(); ii != hitVec.crend(); ++ii) {
4366  iht = *ii;
4367  if(iht > fHits.size()-1) {
4368  mf::LogVerbatim("CC")<<"FitClusterMid bad iht "<<iht;
4369  return;
4370  }
4371  // look for the desired first hit. Use this as the origin wire
4372  if(iht == ihtin) {
4373  UseEm = true;
4374  wire0 = fHits[iht].WireID().Wire;
4375  }
4376  // use hits after finding the first desired hit
4377  if(UseEm) {
4378  xwir.push_back((float)fHits[iht].WireID().Wire - wire0);
4379  ytim.push_back(fHits[iht].PeakTime());
4380  float terr = fHitErrFac * fHits[iht].RMS();
4381  ytimerr2.push_back(terr * terr);
4382  fAveChg += fHits[iht].Integral();
4383  ++hitcnt;
4384  if(hitcnt == usnhit) break;
4385  }
4386  }
4387  nht = hitcnt;
4388  }
4389 
4390  if(nht < 2) return;
4391  fAveChg = fAveChg / (float)nht;
4392  fChgSlp = 0.;
4393 
4394  float intcpt = 0.;
4395  float slope = 0.;
4396  float intcpterr = 0.;
4397  float slopeerr = 0.;
4398  float chidof = 0.;
4399  fLinFitAlg.LinFit(xwir, ytim, ytimerr2, intcpt, slope, intcpterr, slopeerr, chidof);
4400  clChisq = chidof;
4401  if(clChisq > fChiCut[0]) return;
4402  clpar[0] = intcpt;
4403  clpar[1] = slope;
4404  clpar[2] = wire0;
4405  clparerr[0] = intcpterr;
4406  clparerr[1] = slopeerr;
4407  }
4408 
4410  void ClusterCrawlerAlg::FitCluster()
4411  {
4412  // Fits the hits on the US end of a cluster. This routine assumes that
4413  // wires are numbered from lower (upstream) to higher (downstream) and
4414  // that the hits in the fclhits vector are sorted so that upstream hits
4415  // are at the end of the vector
4416 
4417 
4418  clChisq = 999.;
4419 
4420  if(pass > fNumPass - 1) {
4421  mf::LogError("CC")<<"FitCluster called on invalid pass "<<pass;
4422  return;
4423  }
4424 
4425  unsigned short ii, nht = 0;
4426  // fit all hits or truncate?
4427  nht = fcl2hits.size();
4428  if(clLA) {
4429  // Fit large angle cluster
4430  if(nht > fLAClusMaxHitsFit) nht = fLAClusMaxHitsFit;
4431  } else {
4432  if(nht > fMaxHitsFit[pass]) nht = fMaxHitsFit[pass];
4433  }
4434  if(nht < 2) return;
4435 
4436  std::vector<float> xwir;
4437  std::vector<float> ytim;
4438  std::vector<float> ytimerr2;
4439  // apply an angle dependent scale factor.
4440  float angfactor = AngleFactor(clpar[1]);
4441 
4442  unsigned int wire;
4443  unsigned int wire0 = fHits[fcl2hits[fcl2hits.size()-1]].WireID().Wire;
4444  unsigned int ihit;
4445  float terr, qave = 0, qwt;
4446  for(ii = 0; ii < nht; ++ii) {
4447  ihit = fcl2hits[fcl2hits.size() - 1 - ii];
4448  qave += fHits[ihit].Integral();
4449  } // ii
4450  qave /= (float)nht;
4451  for(ii = 0; ii < nht; ++ii) {
4452  ihit = fcl2hits[fcl2hits.size() - 1 - ii];
4453  wire = fHits[ihit].WireID().Wire;
4454  xwir.push_back(wire - wire0);
4455  ytim.push_back(fHits[ihit].PeakTime());
4456  // Scale error by hit multiplicity to account for bias in hit
4457  // multiplet fitting
4458  terr = fHitErrFac * fHits[ihit].RMS() * fHits[ihit].Multiplicity();
4459  if(fAveChg > 0) {
4460  // increase the error for large charge hits
4461  qwt = (fHits[ihit].Integral() / qave) - 1;
4462  if(qwt < 1) qwt = 1;
4463  terr *= qwt;
4464  }
4465  if(terr < 0.01) terr = 0.01;
4466  ytimerr2.push_back(angfactor * terr * terr);
4467  }
4468  CalculateAveHitWidth();
4469  if(prt) {
4470  mf::LogVerbatim myprt("CC");
4471  myprt<<"FitCluster W:T ";
4472  unsigned short cnt = 0;
4473  for(std::vector<unsigned int>::reverse_iterator it = fcl2hits.rbegin(); it != fcl2hits.rend(); ++it) {
4474  unsigned int ihit = *it;
4475  unsigned short wire = fHits[ihit].WireID().Wire;
4476  myprt<<wire<<":"<<(short)fHits[ihit].PeakTime()<<" ";
4477  ++cnt;
4478  if(cnt == 8) {
4479  myprt<<" .... ";
4480  break;
4481  }
4482  }
4483  } // prt
4484 
4485  if(xwir.size() < 2) return;
4486 
4487  float intcpt = 0.;
4488  float slope = 0.;
4489  float intcpterr = 0.;
4490  float slopeerr = 0.;
4491  float chidof = 0.;
4492  fLinFitAlg.LinFit(xwir, ytim, ytimerr2, intcpt, slope, intcpterr, slopeerr, chidof);
4493  clChisq = chidof;
4494  if(chidof > fChiCut[0]) return;
4495  clpar[0] = intcpt;
4496  clpar[1] = slope;
4497  clpar[2] = wire0;
4498  clparerr[0] = intcpterr;
4499  clparerr[1] = slopeerr;
4500 
4501  if(prt) mf::LogVerbatim("CC")<<"nht "<<nht<<" fitpar "<<(int)clpar[0]<<"+/-"<<(int)intcpterr
4502  <<" "<<clpar[1]<<"+/-"<<slopeerr<<" clChisq "<<clChisq;
4503 
4504  }
4506  float ClusterCrawlerAlg::AngleFactor(float slope)
4507  {
4508  // returns an angle dependent cluster projection error factor for fitting
4509  // and hit finding
4510 
4511  float slp = std::abs(slope);
4512  if(slp > 15) slp = 15;
4513  // return a value between 1 and 4
4514  float angfac = 1 + 0.03 * slp * slp;
4515  return angfac;
4516  }
4517 
4519  void ClusterCrawlerAlg::CalculateAveHitWidth()
4520  {
4521  fAveHitWidth = 0;
4522  for(unsigned short ii = 0; ii < fcl2hits.size(); ++ii)
4523  fAveHitWidth += fHits[fcl2hits[ii]].EndTick() - fHits[fcl2hits[ii]].StartTick();
4524  fAveHitWidth /= (float)fcl2hits.size();
4525  } // CalculateAveHitWidth
4526 
4528  void ClusterCrawlerAlg::FitClusterChg()
4529  {
4530  // Fits the charge of hits on the fcl2hits vector to a line, or simply
4531  // uses the average of 1 or 2 hits as determined by NHitsAve
4532 
4533  if(fcl2hits.size() == 0) return;
4534  unsigned int ih0 = fcl2hits.size() - 1;
4535 
4536  if(pass >= fNumPass) {
4537  mf::LogError("CC")<<"FitClusterChg bad pass "<<pass;
4538  return;
4539  }
4540 
4541  // Handle Large Angle clusters
4542  if(clLA) {
4543  // simple average of the charge (and the hit width
4544  // while we are here)
4545  unsigned short nhave = fLAClusMaxHitsFit;
4546  if(nhave > fcl2hits.size()) nhave = fcl2hits.size();
4547  fAveChg = 0;
4548  fChgSlp = 0;
4549  fAveHitWidth = 0;
4550  unsigned int iht;
4551  for(unsigned short ii = 0; ii < nhave; ++ii) {
4552  iht = fcl2hits[fcl2hits.size() - 1 - ii];
4553  fAveChg += fHits[iht].Integral();
4554  fAveHitWidth += (fHits[iht].EndTick() - fHits[iht].StartTick());
4555  } // ii
4556  fAveChg /= (float)fcl2hits.size();
4557  fAveHitWidth /= (float)fcl2hits.size();
4558  return;
4559  } // clLA
4560 
4561  // number of hits at the leading edge that we will fit
4562  unsigned short fitLen = fNHitsAve[pass];
4563  // start fitting charge when there are at least 6 hits if we are tracking
4564  // long clusters
4565  if(fitLen > 5 && // Fit 6 hits when tracking long clusters AND
4566  fcl2hits.size() > 5 && // there are at least 6 hits AND
4567  fcl2hits.size() < fitLen) // there are less than fNHitsAve[pass]
4568  fitLen = 5;
4569 
4570  // don't find the average charge --> no charge cut is made
4571  if(fNHitsAve[pass] < 1) return;
4572 
4573  if(fNHitsAve[pass] == 1) {
4574  // simply use the charge and width the last hit
4575  fAveChg = fHits[fcl2hits[ih0]].Integral();
4576  fChgSlp = 0.;
4577  } else if(fNHitsAve[pass] == 2) {
4578  // average the last two points if requested
4579  fAveChg = (fHits[fcl2hits[ih0]].Integral() +
4580  fHits[fcl2hits[ih0 - 1]].Integral()) / 2.;
4581  fChgSlp = 0.;
4582  } else if((unsigned short)fcl2hits.size() > fitLen){
4583  // do a real fit
4584  std::vector<float> xwir;
4585  std::vector<float> ychg;
4586  std::vector<float> ychgerr2;
4587  // origin of the fit
4588  unsigned int wire0 = fHits[fcl2hits[fcl2hits.size()-1]].WireID().Wire;
4589  // find the mean and rms of the charge
4590  unsigned short npt = 0;
4591  unsigned short imlast = 0;
4592  float ave = 0.;
4593  float rms = 0.;
4594  // this loop intentionally ignores the Begin hit
4595  for(unsigned int ii = fcl2hits.size() - 1; ii > 0; --ii) {
4596  ++npt;
4597  float chg = fHits[fcl2hits[ii]].Integral();
4598  ave += chg;
4599  rms += chg * chg;
4600  if(npt == fitLen) {
4601  imlast = ii;
4602  break;
4603  }
4604  }
4605  float fnpt = npt;
4606  ave /= fnpt;
4607  rms = std::sqrt((rms - fnpt * ave * ave) / (fnpt - 1));
4608  float chgcut = ave + rms;
4609  float chg;
4610  unsigned int wire;
4611  for(unsigned short ii = fcl2hits.size() - 1; ii > imlast; --ii) {
4612  wire = fHits[fcl2hits[ii]].WireID().Wire;
4613  chg = fHits[fcl2hits[ii]].Integral();
4614  if(chg > chgcut) continue;
4615  xwir.push_back((float)(wire - wire0));
4616  ychg.push_back(chg);
4617  ychgerr2.push_back(chg);
4618  }
4619  if(ychg.size() < 3) return;
4620  float intcpt; float slope; float intcpterr;
4621  float slopeerr; float chidof;
4622  fLinFitAlg.LinFit(xwir, ychg, ychgerr2, intcpt, slope, intcpterr, slopeerr, chidof);
4623  if(prt) mf::LogVerbatim("CC")<<"FitClusterChg wire "<<wire0
4624  <<" chidof "<<(int)chidof<<" npt "<<xwir.size()
4625  <<" charge = "<<(int)intcpt<<" slope = "<<(int)slope
4626  <<" first ave "<<(int)ave<<" rms "<<(int)rms;
4627  if(chidof > 100.) return;
4628  // fit must have gone wrong if the fStepCrawlChgRatCut average is greater than
4629  // the average using all points
4630  if(intcpt > ave) return;
4631  // ensure that change does not exceed 30%
4632  if(fAveChg > 0) {
4633  ave = intcpt / fAveChg;
4634  if(ave > 1.3) return;
4635  if(ave < 0.77) return;
4636  }
4637  fAveChg = intcpt;
4638  fChgSlp = slope;
4639  }
4640  } // fitchg
4641 
4643  void ClusterCrawlerAlg::AddLAHit(unsigned int kwire, bool& ChkCharge, bool& HitOK, bool& SigOK)
4644  {
4645  // A variant of AddHit for large angle clusters
4646 
4647  SigOK = false;
4648  HitOK = false;
4649 
4650  // not in the range of wires with hits
4651  if(kwire < fFirstWire || kwire > fLastWire) return;
4652 
4653  if(fcl2hits.size() == 0) return;
4654 
4655  // skip bad wire and assume the track was there
4656  if(WireHitRange[kwire].first == -1) {
4657  SigOK = true;
4658  return;
4659  }
4660  // return SigOK false if no hit on a good wire
4661  if(WireHitRange[kwire].first == -2) return;
4662 
4663  unsigned int firsthit = WireHitRange[kwire].first;
4664  unsigned int lasthit = WireHitRange[kwire].second;
4665 
4666  // max allowable time difference between projected cluster and a hit
4667  float timeDiff = 40 * AngleFactor(clpar[1]);
4668  float dtime;
4669 
4670  // the last hit added to the cluster
4671  unsigned int lastClHit = UINT_MAX;
4672  if(fcl2hits.size() > 0) {
4673  lastClHit = fcl2hits[fcl2hits.size()-1];
4674  if(lastClHit == 0) {
4675  fAveChg = fHits[lastClHit].Integral();
4676  fAveHitWidth = fHits[lastClHit].EndTick() - fHits[lastClHit].StartTick();
4677  }
4678  } // fcl2hits.size() > 0
4679 
4680  // the projected time of the cluster on this wire
4681  float prtime = clpar[0] + ((float)kwire - clpar[2]) * clpar[1];
4682  float chgWinLo = prtime - fChgNearWindow;
4683  float chgWinHi = prtime + fChgNearWindow;
4684  float chgrat, hitWidth;
4685  float hitWidthCut = 0.5 * fAveHitWidth;
4686  float cnear = 0;
4687 // float fom;
4688  if(prt) mf::LogVerbatim("CC")<<"AddLAHit: wire "<<kwire<<" prtime "<<prtime<<" max timeDiff "<<timeDiff<<" fAveChg "<<fAveChg;
4689  unsigned int imbest = 0;
4690  unsigned int khit;
4691  for(khit = firsthit; khit < lasthit; ++khit) {
4692  // obsolete hit?
4693  if(inClus[khit] < 0) continue;
4694  dtime = std::abs(fHits[khit].PeakTime() - prtime);
4695 // fom = dtime;
4696  hitWidth = fHits[khit].EndTick() - fHits[khit].StartTick();
4697  chgrat = 1;
4698  if(ChkCharge && fAveChg > 0) {
4699  chgrat = fHits[khit].Integral() / fAveChg;
4700 // fom *= std::abs(chgrat - 1);
4701 // fom *= fAveHitWidth / hitWidth;
4702  }
4703  if(prt) mf::LogVerbatim("CC")
4704  <<" Chk W:T "<<kwire<<":"<<(short)fHits[khit].PeakTime()
4705  <<" dT "<<std::fixed<<std::setprecision(1)<<dtime
4706  <<" InClus "<<inClus[khit]
4707  <<" mult "<<fHits[khit].Multiplicity()
4708  <<" width "<<(int)hitWidth
4709  <<" MergeAvail "<<mergeAvailable[khit]
4710  <<" Chi2 "<<std::fixed<<std::setprecision(2)<<fHits[khit].GoodnessOfFit()
4711  <<" Charge "<<(int)fHits[khit].Integral()
4712  <<" chgrat "<<std::fixed<<std::setprecision(1)<<chgrat
4713 // <<" fom "<<std::fixed<<std::setprecision(1)<<fom
4714 // <<" LoT "<<(int)fHits[khit].StartTick()
4715 // <<" HiT "<<(int)fHits[khit].EndTick()
4716  <<" index "<<khit;
4717  // count charge in the window
4718  if(fHits[khit].PeakTime() > chgWinLo && fHits[khit].PeakTime() < chgWinHi) cnear += fHits[khit].Integral();
4719  // projected time outside the Signal time window?
4720  if(prtime < fHits[khit].StartTick() - timeDiff) continue;
4721  if(prtime > fHits[khit].EndTick() + timeDiff) continue;
4722  SigOK = true;
4723  // hit used?
4724  if(inClus[khit] > 0) continue;
4725  // ignore narrow width hits
4726  if(hitWidth < hitWidthCut) continue;
4727  // ignore very low charge hits
4728  if(chgrat < 0.1) continue;
4729 // dtime = std::abs(prtime - fHits[khit].PeakTime());
4730  if(dtime < timeDiff) {
4731  HitOK = true;
4732  imbest = khit;
4733  timeDiff = dtime;
4734  }
4735  } // khit
4736 
4737  if(prt && !HitOK) mf::LogVerbatim("CC")<<" no hit found ";
4738 
4739  if(!HitOK) return;
4740 
4741  if(prt) mf::LogVerbatim("CC")<<" Pick hit time "<<(int)fHits[imbest].PeakTime()<<" hit index "<<imbest;
4742 
4743  // merge hits in a multiplet?
4744  short hnear = 0;
4745  if(lastClHit != UINT_MAX && fHits[imbest].Multiplicity() > 1) {
4746  bool doMerge = true;
4747  // Standard code
4748  // don't merge if we are close to a vertex
4749  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
4750  if(vtx[ivx].CTP != clCTP) continue;
4751  if(prt) mf::LogVerbatim("CC")<<" close vtx chk W:T "<<vtx[ivx].Wire<<":"<<(int)vtx[ivx].Time;
4752  if(std::abs(kwire - vtx[ivx].Wire) < 5 && std::abs(int(fHits[imbest].PeakTime() - vtx[ivx].Time)) < 20 ) {
4753  if(prt) mf::LogVerbatim("CC")<<" Close to a vertex. Don't merge hits";
4754  doMerge = false;
4755  }
4756  } // ivx
4757  // Decide which hits in the multiplet to merge. Hits that are well
4758  // separated from each other should not be merged
4759  if(doMerge) {
4760  unsigned short nused = 0;
4761  // the total charge of the hit multiplet
4762  float multipletChg = 0.;
4763  float chicut = AngleFactor(clpar[1]) * fHitMergeChiCut * fHits[lastClHit].RMS();
4764  // look for a big separation between adjacent hits
4765  std::pair<size_t, size_t> MultipletRange = FindHitMultiplet(imbest);
4766  for(size_t jht = MultipletRange.first; jht < MultipletRange.second; ++jht) {
4767  if(inClus[jht] < 0) continue;
4768  if(inClus[jht] == 0) multipletChg += fHits[jht].Integral();
4769  else ++nused;
4770  // check the neighbor hit separation
4771  if(jht > MultipletRange.first) {
4772  // pick the larger RMS of the two hits
4773  float hitRMS = fHits[jht].RMS();
4774  if(fHits[jht - 1].RMS() > hitRMS) hitRMS = fHits[jht-1].RMS();
4775  const float tdiff = std::abs(fHits[jht].PeakTime() - fHits[jht-1].PeakTime()) / hitRMS;
4776  if(prt) mf::LogVerbatim("CC")<<" Hit RMS chisq "<<tdiff<<" chicut "<<chicut;
4777  if(tdiff > chicut) doMerge = false;
4778  } // jht > 0
4779  } // jht
4780  if(prt) {
4781  if(!doMerge) mf::LogVerbatim("CC")
4782  <<" Hits are well separated. Don't merge them ";
4783  }
4784  if(doMerge && nused == 0) {
4785  // compare the charge with the last hit added?
4786  if(ChkCharge) {
4787  // there is a nearby hit
4788  hnear = 1;
4789  float chgrat = multipletChg / fHits[lastClHit].Integral();
4790  if(prt) mf::LogVerbatim("CC")<<" merge hits charge check "
4791  <<(int)multipletChg<<" Previous hit charge "<<(int)fHits[lastClHit].Integral();
4792  if(chgrat > 2) doMerge = false;
4793  }
4794  } // doMerge && nused == 0
4795  } // doMerge true
4796  if(doMerge) {
4797  // there is a nearby hit and it will be merged
4798  hnear = -1;
4799  bool didMerge;
4800  MergeHits(imbest, didMerge);
4801  } // doMerge
4802  } // Hits[imbest].Multiplicity() > 1
4803 
4804  // attach to the cluster and fit
4805  fcl2hits.push_back(imbest);
4806  FitCluster();
4807  FitClusterChg();
4808  chifits.push_back(clChisq);
4809  hitNear.push_back(hnear);
4810  // remove the charge of the just added hit
4811  cnear -= fHits[imbest].Integral();
4812  if(cnear < 0) cnear = 0;
4813  // divide by the just added hit charge
4814  cnear /= fHits[imbest].Integral();
4815  chgNear.push_back(cnear);
4816  if(prt) {
4817  hitWidth = fHits[imbest].EndTick() - fHits[imbest].StartTick();
4818  mf::LogVerbatim("CC")
4819  <<" >>LADD"<<pass<<" W:T "<<PrintHit(imbest)
4820  <<" dT "<<timeDiff
4821  <<" clChisq "<<clChisq
4822  <<" Chg "<<(int)fHits[imbest].Integral()
4823  <<" AveChg "<<(int)fAveChg
4824  <<" width "<<(int)hitWidth
4825  <<" AveWidth "<<(int)fAveHitWidth
4826  <<" fcl2hits size "<<fcl2hits.size();
4827  } // prt
4828  // decide what to do with a bad fit
4829  if(clChisq > fChiCut[pass]) {
4830  FclTrimUS(1);
4831  FitCluster();
4832  HitOK = false;
4833 // if(prt) mf::LogVerbatim("CC")<<" LADD- Removed hit. New clChisq "<<std::setprecision(3)<<clChisq<<" nhits "<<fcl2hits.size();
4834  SigOK = false;
4835  if(prt) mf::LogVerbatim("CC")<<" LADD- Removed bad hit. Stopped tracking";
4836  }
4837 /*
4838  // stop tracking if previous chisq values were close to the cut.
4839  // this is an indicator that the track is wandering too much for this pass
4840  if(chifits.size() > 2 &&
4841  chifits[chifits.size()-2] > 0.8 * fChiCut[pass]) SigOK = false;
4842  if(prt) mf::LogVerbatim("CC")<<" Set SigOK = "<<SigOK;
4843 */
4844  } // AddLAHit()
4845 
4847  bool ClusterCrawlerAlg::ClusterHitsOK(short nHitChk)
4848  {
4849  // Check StartTick and EndTick of hits on adjacent wires overlap as illustrated below.
4850  // >>>>>> This is OK
4851  // Wire StartTick EndTick
4852  // n |--------------|
4853  // n+1 |--------------|
4854  // n+2 |--------------|
4855  // >>>>>> This is NOT OK
4856  // n |------|
4857  // n+1 |-----|
4858  // n+2 |------|
4859 
4860  if(fcl2hits.size() == 0) return true;
4861 // if(fAveHitWidth == 0) return true;
4862 
4863  unsigned short nHitToChk = fcl2hits.size();
4864  if(nHitChk > 0) nHitToChk = nHitChk + 1;
4865  unsigned short ii, indx;
4866 
4867  // require that they overlap
4868  // add a tolerance to the StartTick - EndTick overlap
4869  raw::TDCtick_t tol = 30;
4870  // expand the tolerance for induction planes
4871  if(plane < geom->Cryostat(cstat).TPC(tpc).Nplanes()-1) tol = 40;
4872 
4873  bool posSlope = (fHits[fcl2hits[0]].PeakTime() > fHits[fcl2hits[fcl2hits.size() - 1]].PeakTime());
4874 
4875  if(prt) {
4876  for(ii = 0; ii < nHitToChk; ++ii) {
4877  indx = fcl2hits.size() - 1 - ii;
4878  mf::LogVerbatim("CC")<<"ClusterHitsOK chk "<<fHits[fcl2hits[indx]].WireID().Wire<<" start "<<fHits[fcl2hits[indx]].StartTick()<<" peak "<<fHits[fcl2hits[indx]].PeakTime()<<" end "<<fHits[fcl2hits[indx]].EndTick()<<" posSlope "<<posSlope;
4879  }
4880  }
4881 
4882  raw::TDCtick_t hiStartTick, loEndTick;
4883  for(ii = 0; ii < nHitToChk - 1; ++ii) {
4884  indx = fcl2hits.size() - 1 - ii;
4885  // ignore if not on adjacent wires
4886  if(util::absDiff(fHits[fcl2hits[indx]].WireID().Wire, fHits[fcl2hits[indx-1]].WireID().Wire) > 1) continue;
4887  hiStartTick = std::max(fHits[fcl2hits[indx]].StartTick(), fHits[fcl2hits[indx-1]].StartTick());
4888  loEndTick = std::min(fHits[fcl2hits[indx]].EndTick(), fHits[fcl2hits[indx-1]].EndTick());
4889  if(posSlope) {
4890  if(loEndTick + tol < hiStartTick) {
4891 // if(prt) mf::LogVerbatim("CC")<<" bad overlap pos Slope "<<loEndTick<<" > "<<hiStartTick;
4892  return false;
4893  }
4894  } else {
4895  if(loEndTick + tol < hiStartTick) {
4896 // if(prt) mf::LogVerbatim("CC")<<" bad overlap neg Slope "<<loEndTick<<" < "<<hiStartTick;
4897  return false;
4898  }
4899  }
4900  } // ii
4901 // if(prt) mf::LogVerbatim("CC")<<" Cluster hits are OK";
4902  return true;
4903  } // ClusterHitsOK
4904 
4905 
4907  void ClusterCrawlerAlg::AddHit(unsigned int kwire, bool& HitOK, bool& SigOK)
4908  {
4909  // Add a hit to the cluster if it meets several criteria:
4910  // similar pulse height to the cluster (if fAveChg is defined)
4911  // closest hit to the project cluster position.
4912  // Return SigOK if there is a nearby hit that was missed due to the cuts
4913 
4914  SigOK = false;
4915  HitOK = false;
4916 
4917  // not in the range of wires with hits
4918  if(kwire < fFirstWire || kwire > fLastWire) return;
4919 
4920  unsigned int lastClHit = UINT_MAX;
4921  if(fcl2hits.size() > 0) lastClHit = fcl2hits[fcl2hits.size()-1];
4922 
4923  // the last hit added to the cluster
4924  unsigned int wire0 = clpar[2];
4925 
4926  // return if no signal and no hit
4927  if(fAllowNoHitWire == 0) {
4928  if(WireHitRange[kwire].first == -2) return;
4929  } else {
4930  // allow a number of wires with no hits
4931  if(WireHitRange[kwire].first == -2 &&
4932  (wire0 - kwire) > fAllowNoHitWire) {
4933  SigOK = true;
4934  return;
4935  }
4936  }
4937  // skip bad wire, but assume the track was there
4938  if(WireHitRange[kwire].first == -1) {
4939  SigOK = true;
4940  return;
4941  }
4942 
4943  unsigned int firsthit = WireHitRange[kwire].first;
4944  unsigned int lasthit = WireHitRange[kwire].second;
4945 
4946  // the projected time of the cluster on this wire
4947  float dw = (float)kwire - (float)wire0;
4948  float prtime = clpar[0] + dw * clpar[1];
4949  if(prtime < 0 || (unsigned int)prtime > fMaxTime) return;
4950  // Find the projected time error including the projection error and the
4951  // error from the last hit added
4952  float prtimerr2 = std::abs(dw)*clparerr[1]*clparerr[1];
4953 
4954  // apply an angle dependent scale factor to the hit error. Default is very large error
4955  float hiterr = 10;
4956  if(lastClHit != UINT_MAX) hiterr = 3 * fHitErrFac * fHits[lastClHit].RMS();
4957  float err = std::sqrt(prtimerr2 + hiterr * hiterr);
4958  // Time window for accepting a hit.
4959  float hitWin = fClProjErrFac * err;
4960 
4961  float prtimeLo = prtime - hitWin;
4962  float prtimeHi = prtime + hitWin;
4963  float chgWinLo = prtime - fChgNearWindow;
4964  float chgWinHi = prtime + fChgNearWindow;
4965  if(prt) {
4966  mf::LogVerbatim("CC")<<"AddHit: wire "<<kwire<<" prtime Lo "<<(int)prtimeLo<<" prtime "<<(int)prtime<<" Hi "<<(int)prtimeHi<<" prtimerr "<<sqrt(prtimerr2)<<" hiterr "<<hiterr<<" fAveChg "<<(int)fAveChg<<" fAveHitWidth "<<std::setprecision(3)<<fAveHitWidth;
4967  }
4968  // loop through the hits
4969  unsigned int imbest = INT_MAX;
4970  float best = 9999., dtime;
4971  float cnear = 0;
4972  float hitTime, hitChg, hitStartTick, hitEndTick;
4973  for(unsigned int khit = firsthit; khit < lasthit; ++khit) {
4974  // obsolete hit?
4975  if(inClus[khit] < 0) continue;
4976  hitTime = fHits[khit].PeakTime();
4977  dtime = std::abs(hitTime - prtime);
4978  if(dtime > 1000) continue;
4979  hitStartTick = fHits[khit].StartTick();
4980  hitEndTick = fHits[khit].EndTick();
4981  // weight by the charge difference
4982  if(fAveChg > 0) dtime *= std::abs(fHits[khit].Integral() - fAveChg) / fAveChg;
4983  if(prt && std::abs(dtime) < 100) {
4984  mf::LogVerbatim("CC")
4985  <<" Chk W:T "<<PrintHit(khit)
4986  <<" dT "<<std::fixed<<std::setprecision(1)<<(hitTime - prtime)
4987  <<" InClus "<<inClus[khit]
4988  <<" mult "<<fHits[khit].Multiplicity()
4989  <<" RMS "<<std::fixed<<std::setprecision(1)<<fHits[khit].RMS()
4990  <<" Chi2 "<<std::fixed<<std::setprecision(1)<<fHits[khit].GoodnessOfFit()
4991  <<" Charge "<<(int)fHits[khit].Integral()
4992  <<" Peak "<<std::fixed<<std::setprecision(1)<<fHits[khit].PeakAmplitude()
4993  <<" LoT "<<(int)hitStartTick
4994  <<" HiT "<<(int)hitEndTick
4995  <<" index " << khit;
4996  }
4997  // count charge in the window
4998  if(fHits[khit].StartTick() > chgWinLo && fHits[khit].EndTick() < chgWinHi) cnear += fHits[khit].Integral();
4999  // check for signal
5000  if(prtimeHi < hitStartTick) continue;
5001  if(prtimeLo > hitEndTick) continue;
5002  SigOK = true;
5003  // check for good hit
5004  if(hitTime < prtimeLo) continue;
5005  if(hitTime > prtimeHi) continue;
5006  // hit used?
5007  if(inClus[khit] > 0) continue;
5008  if(dtime < best) {
5009  best = dtime;
5010  imbest = khit;
5011  }
5012  } // khit
5013 
5014  if(!SigOK) {
5015  if(fAllowNoHitWire == 0) return;
5016  if(prt) mf::LogVerbatim("CC")<<" wire0 "<<wire0<<" kwire "<<kwire<<" max "<<fAllowNoHitWire<<" imbest "<<imbest;
5017  if((wire0 - kwire) > fAllowNoHitWire) return;
5018  SigOK = true;
5019  }
5020 
5021  if(imbest == INT_MAX) return;
5022 
5023  recob::Hit const& hit = fHits[imbest];
5024  hitChg = hit.Integral();
5025 
5026  if(prt) mf::LogVerbatim("CC")<<" Best hit time "<<(int)hit.PeakTime();
5027 
5028  short hnear = 0;
5029  // merge hits in a doublet?
5030  bool didMerge = false;
5031  if(lastClHit != UINT_MAX && fAveHitWidth > 0 && fHitMergeChiCut > 0 && hit.Multiplicity() == 2) {
5032  bool doMerge = true;
5033  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
5034  if(std::abs(kwire - vtx[ivx].Wire) < 10 &&
5035  std::abs(int(hit.PeakTime() - vtx[ivx].Time)) < 20 )
5036  {
5037  doMerge = false;
5038  break;
5039  }
5040  } // ivx
5041  // quit if localindex does not make sense.
5042  if (hit.LocalIndex() != 0 && imbest == 0) doMerge = false;
5043  if (doMerge) {
5044  // find the neighbor hit
5045  unsigned int oht;
5046  if(hit.LocalIndex() == 0) {
5047  oht = imbest + 1;
5048  } else {
5049  oht = imbest - 1;
5050  } // hit.LocalIndex() == 0
5051  // check the hit time separation
5052  recob::Hit const& other_hit = fHits[oht];
5053  float hitSep = std::abs(hit.PeakTime() - other_hit.PeakTime());
5054  hitSep /= hit.RMS();
5055  // check the the charge similarity
5056  float totChg = hitChg + other_hit.Integral();
5057  float lastHitChg = fAveChg;
5058  if(lastHitChg < 0) lastHitChg = fHits[lastClHit].Integral();
5059  hnear = 1;
5060  if(prt) mf::LogVerbatim("CC")<<" Chk hit merge hitsep "<<hitSep<<" dChg "<<std::abs(totChg - lastHitChg)<<" Cut "<<std::abs(hit.Integral() - lastHitChg);
5061 // if(inClus[oht] == 0 && hitSep < fHitMergeChiCut && std::abs(totChg - lastHitChg) < std::abs(hit.Integral() - lastHitChg)) {
5062  if(inClus[oht] == 0 && hitSep < fHitMergeChiCut) {
5063  if(prt) mf::LogVerbatim("CC")<<" Merging hit doublet "<<imbest;
5064  MergeHits(imbest, didMerge);
5065  if(prt && !didMerge) mf::LogVerbatim("CC")<<" Hit merge failed ";
5066  } // not in a cluster, hitSep OK, total charge OK
5067  } // doMerge
5068  } // fHitMergeChiCut > 0 && hit.Multiplicity() == 2
5069 
5070  // Make a charge similarity cut if the average charge is defined
5071  bool fitChg = true;
5072  if(fAveChg > 0.) {
5073 
5074  float chgrat = (hitChg - fAveChg) / fAveChg;
5075  if(prt) mf::LogVerbatim("CC")<<" Chgrat "<<std::setprecision(2)<<chgrat;
5076 
5077  // charge is way too high?
5078  if(chgrat > 3 * fChgCut[pass]) {
5079  if(prt) mf::LogVerbatim("CC")<<" fails 3 x high charge cut "<<fChgCut[pass]<<" on pass "<<pass;
5080  return;
5081  }
5082 
5083  // Determine if the last hit added was a large (low) charge hit
5084  // This will be used to prevent adding large (low) charge hits on two
5085  // consecutive fits. This cut is only applied to hits on adjacent wires
5086  float bigchgcut = 1.5 * fChgCut[pass];
5087  bool lasthitbig = false;
5088  bool lasthitlow = false;
5089  if(lastClHit != UINT_MAX && util::absDiff(wire0, kwire) == 1) {
5090  float lastchgrat = (fHits[lastClHit].Integral() - fAveChg) / fAveChg;
5091  lasthitbig = ( lastchgrat > bigchgcut);
5092  lasthitlow = ( lastchgrat < -fChgCut[pass]);
5093  }
5094 
5095  // the last hit added was low charge and this one is as well
5096  if(lasthitlow && chgrat < -fChgCut[pass]) {
5097  if(prt) mf::LogVerbatim("CC")<<" fails low charge cut. Stop crawling.";
5098  SigOK = false;
5099  return;
5100  } // lasthitlow
5101 
5102  // the last hit was high charge and this one is also
5103  if(lasthitbig && chgrat > fChgCut[pass]) {
5104  if(prt) mf::LogVerbatim("CC")<<" fails 2nd hit high charge cut. Last hit was high also. ";
5105  return;
5106  } // lasthitbig
5107 
5108 
5109  // require that large charge hits have a very good projection error
5110  if(chgrat > fChgCut[pass]) {
5111  if(best > 2 * err) {
5112  if(prt) mf::LogVerbatim("CC")<<" high charge && bad dT= "<<best<<" err= "<<err;
5113  return;
5114  }
5115  } // chgrat > fChgCut[pass]
5116 
5117  // decide whether to fit the charge
5118  fitChg = (chgrat < std::abs(fChgCut[pass]) );
5119  } // fAveChg > 0
5120 
5121  // we now have a hit that meets all the criteria. Fit it
5122  fcl2hits.push_back(imbest);
5123  // This is strictly only necessary when calling AddHit for seed clusters
5124  if(fcl2hits.size() == 3) std::sort(fcl2hits.begin(), fcl2hits.end(), SortByLowHit);
5125  FitCluster();
5126  chifits.push_back(clChisq);
5127  hitNear.push_back(hnear);
5128  // remove the charge of the just added hit
5129  cnear -= fHits[imbest].Integral();
5130  if(cnear < 0) cnear = 0;
5131  // divide by the just added hit charge
5132  cnear /= fHits[imbest].Integral();
5133  chgNear.push_back(cnear);
5134  // nearby hit check
5135 // ChkClusterNearbyHits(prt);
5136  HitOK = true;
5137 
5138  if(chgNear.size() != fcl2hits.size()) {
5139  mf::LogError("CC")<<"AddHit: Bad length";
5140  return;
5141  }
5142 
5143  if(prt) mf::LogVerbatim("CC")<<" >>ADD"<<pass
5144  <<" W:T "<<PrintHit(imbest)
5145  <<" dT "<<best<<" clChisq "<<clChisq
5146  <<" Chg "<<(int)fHits[imbest].Integral()
5147  <<" AveChg "<<(int)fAveChg
5148  // <<" width "<<(int)hitWidth<<" fAveHitWidth "<<(int)fAveHitWidth
5149  <<" fcl2hits size "<<fcl2hits.size();
5150 
5151  if(!fitChg) return;
5152  if(prt) mf::LogVerbatim("CC")<<" Fit charge ";
5153  FitClusterChg();
5154  } // AddHit()
5155 
5156 
5158  void ClusterCrawlerAlg::ChkClusterNearbyHits(bool prt)
5159  {
5160  // analyze the hitnear vector
5161  // 0 = no nearby hit exists
5162  // 1 = a nearby hit exists but was not merged
5163  // -1 = a nearby hit was merged
5164 
5165  if(fHitMergeChiCut <= 0) return;
5166 
5167  if(hitNear.size() != fcl2hits.size()) {
5168  mf::LogWarning("CC")<<"Coding error: hitNear size != fcl2hits";
5169  return;
5170  }
5171 
5172  // Analyze the last 6 hits added but don't consider the first few hits
5173  if(hitNear.size() < 12) return;
5174 
5175  // TODO move into loops
5176  unsigned short ii, indx;
5177  unsigned short merged = 0;
5178  unsigned short notmerged = 0;
5179  for(ii = 0; ii < 6; ++ii) {
5180  indx = hitNear.size() - 1 - ii;
5181  if(hitNear[indx] > 0) ++notmerged;
5182  if(hitNear[indx] < 0) ++merged;
5183  }
5184 
5185  if(prt) mf::LogVerbatim("CC")<<"ChkClusterNearbyHits: nearby hits merged "<<merged<<" not merged "<<notmerged;
5186 
5187  if(notmerged < 2) return;
5188 
5189  // a number of nearby hits were not merged while crawling, so the
5190  // average charge is probably wrong. Look at the last 6 hits added
5191  // and merge them if they are close
5192  bool didMerge;
5193  for(ii = 0; ii < 6; ++ii) {
5194  indx = fcl2hits.size() - 1 - ii;
5195  const unsigned int iht = fcl2hits[indx];
5196  recob::Hit const& hit = fHits[iht];
5197  if(hit.Multiplicity() == 2) {
5198  // quit if localindex does not make sense.
5199  if (hit.LocalIndex() != 0 && iht == 0) continue;
5200  // hit doublet. Get the index of the other hit
5201  unsigned int oht;
5202  if(hit.LocalIndex() == 0) {
5203  oht = iht + 1;
5204  } else {
5205  oht = iht - 1;
5206  } // hit.LocalIndex() == 0
5207  recob::Hit const& other_hit = fHits[oht];
5208  // TODO use Hit::TimeDistanceAsRMS()
5209  float hitSep = std::abs(hit.PeakTime() - other_hit.PeakTime());
5210  hitSep /= hit.RMS();
5211  if(hitSep < fHitMergeChiCut && inClus[oht] == 0) {
5212  if(prt) mf::LogVerbatim("CC")<<"Merging hit doublet "
5213  <<iht<<" W:T "<<fHits[iht].WireID().Wire<<":"<<fHits[iht].PeakTime();
5214  MergeHits(iht, didMerge);
5215  if(didMerge) hitNear[indx] = -1;
5216  } // hitSep OK and not in a cluster
5217  } // hit doublet
5218  } // ii
5219 
5220  // now re-fit
5221  FitCluster();
5222  FitClusterChg();
5223 
5224  if(prt) mf::LogVerbatim("CC")<<"ChkClusterNearbyHits refit cluster. fAveChg= "<<fAveChg;
5225 
5226  } // ChkClusterHitNear()
5227 
5229  void ClusterCrawlerAlg::FitVtx(unsigned short iv)
5230  {
5231  std::vector<float> x;
5232  std::vector<float> y;
5233  std::vector<float> ey2;
5234  float arg;
5235 
5236  // don't fit fixed vertices
5237  if(vtx[iv].Fixed) return;
5238 
5239  // Set this large in case something bad happens
5240  vtx[iv].ChiDOF = 99;
5241 
5242  // make a list of clusters
5243  unsigned short icl;
5244  std::vector<unsigned short> vcl;
5245  for(icl = 0; icl < tcl.size(); ++icl) {
5246  if(tcl[icl].ID < 0) continue;
5247  if(tcl[icl].CTP != vtx[iv].CTP) continue;
5248  if(tcl[icl].EndVtx == iv) vcl.push_back(icl);
5249  if(tcl[icl].BeginVtx == iv) vcl.push_back(icl);
5250  }
5251 
5252  vtx[iv].NClusters = vcl.size();
5253 
5254  if(vcl.size() == 0) return;
5255 
5256  // don't let the time error be less than the expected
5257  // time error of hits on a cluster. This is crude by
5258  // probably good enough
5259  icl = vcl[0];
5260  unsigned short indx = tcl[icl].tclhits.size() - 1;
5261  unsigned int hit = tcl[icl].tclhits[indx];
5262  float minTimeErr = fHitErrFac * fHits[hit].RMS() * fHits[hit].Multiplicity();
5263 
5264  if(vcl.size() == 1) {
5265  icl = vcl[0];
5266  // Put the vertex at the appropriate end of the cluster
5267  if(tcl[icl].EndVtx == iv) {
5268  vtx[iv].Wire = tcl[icl].EndWir;
5269  vtx[iv].WireErr = 1;
5270  vtx[iv].Time = tcl[icl].EndTim;
5271  // set the vertex time error to the hit error used for fitting
5272  indx = tcl[icl].tclhits.size() - 1;
5273  hit = tcl[icl].tclhits[indx];
5274  vtx[iv].TimeErr = fHitErrFac * fHits[hit].RMS() * fHits[hit].Multiplicity();
5275  vtx[iv].ChiDOF = 0;
5276  }
5277  if(tcl[icl].BeginVtx == iv) {
5278  vtx[iv].Wire = tcl[icl].BeginWir;
5279  vtx[iv].WireErr = 1;
5280  vtx[iv].Time = tcl[icl].BeginTim;
5281  // set the vertex time error to the hit error used for fitting
5282  hit = tcl[icl].tclhits[0];
5283  vtx[iv].TimeErr = fHitErrFac * fHits[hit].RMS() * fHits[hit].Multiplicity();
5284  vtx[iv].ChiDOF = 0;
5285  }
5286  return;
5287  } // size 1
5288 
5289  std::vector<double> slps;
5290  std::vector<double> slperrs;
5291  for(unsigned short ii = 0; ii < vcl.size(); ++ii) {
5292  icl = vcl[ii];
5293  if(tcl[icl].EndVtx == iv) {
5294  x.push_back(tcl[icl].EndSlp);
5295  slps.push_back(tcl[icl].EndSlp);
5296  slperrs.push_back(tcl[icl].EndSlpErr);
5297  arg = tcl[icl].EndSlp * tcl[icl].EndWir - tcl[icl].EndTim;
5298  y.push_back(arg);
5299  if(tcl[icl].EndSlpErr > 0.) {
5300  arg = tcl[icl].EndSlpErr * tcl[icl].EndWir;
5301  } else {
5302  arg = .1 * tcl[icl].EndWir;
5303  }
5304  ey2.push_back(arg * arg);
5305  } else if(tcl[icl].BeginVtx == iv) {
5306  x.push_back(tcl[icl].BeginSlp);
5307  slps.push_back(tcl[icl].BeginSlp);
5308  slperrs.push_back(tcl[icl].BeginSlpErr);
5309  arg = tcl[icl].BeginSlp * tcl[icl].BeginWir - tcl[icl].BeginTim;
5310  y.push_back(arg);
5311  if(tcl[icl].BeginSlpErr > 0.) {
5312  arg = tcl[icl].BeginSlpErr * tcl[icl].BeginWir;
5313  } else {
5314  arg = .1 * tcl[icl].BeginWir;
5315  }
5316  ey2.push_back(arg * arg);
5317  }
5318  } // ii
5319  if(x.size() < 2) return;
5320 
5321  // calculate error
5322  double sumerr = 0, cnt = 0;
5323  for(unsigned short ii = 0; ii < slps.size() - 1; ++ii) {
5324  for(unsigned short jj = ii + 1; jj < slps.size(); ++jj) {
5325  arg = std::min(slperrs[ii], slperrs[jj]);
5326  arg /= (slps[ii] - slps[jj]);
5327  sumerr += arg * arg;
5328  ++cnt;
5329  } // jj
5330  } // ii
5331  sumerr /= cnt;
5332 
5333  float vTime = 0.;
5334  float vTimeErr = 0.;
5335  float vWire = 0.;
5336  float vWireErr = 0.;
5337  float chiDOF;
5338  fLinFitAlg.LinFit(x, y, ey2, vTime, vWire, vTimeErr, vWireErr, chiDOF);
5339  if(chiDOF > 900) return;
5340  vTime = -vTime;
5341  // a crazy time from the fit?
5342  if(vTime < 0 || vTime > fMaxTime) return;
5343  // a crazy wire from the fit?
5344  geo::PlaneID iplID = DecodeCTP(vtx[iv].CTP);
5345  if(vWire < 0 || vWire > geom->Nwires(iplID.Plane, iplID.TPC, iplID.Cryostat)) return;
5346  vtx[iv].ChiDOF = chiDOF;
5347  vtx[iv].Wire = vWire;
5348  vtx[iv].Time = vTime;
5349  vtx[iv].WireErr = vWire * sqrt(sumerr);
5350  vtx[iv].TimeErr = vTime * fabs(sumerr);
5351 // std::cout<<"vWire "<<vWire<<" Err "<<vtx[iv].WireErr<<" vTime "<<vTime<<" Err "<<vtx[iv].TimeErr
5352 // <<" sumerr "<<sumerr<<" nclusters "<<x.size()<<"\n";
5353  // set minimum wire error
5354  if(vtx[iv].WireErr < 1) vtx[iv].WireErr = 2;
5355  // set minimum time error
5356  if(vtx[iv].TimeErr < minTimeErr) vtx[iv].TimeErr = minTimeErr;
5357 
5358  } // FitVtx
5359 
5361  void ClusterCrawlerAlg::Vtx3ClusterMatch(geo::TPCID const& tpcid)
5362  {
5363  // Look for clusters that end/begin near the expected wire/time
5364  // for incomplete 3D vertices
5365  if(vtx3.size() == 0) return;
5366 
5367  const unsigned int cstat = tpcid.Cryostat;
5368  const unsigned int tpc = tpcid.TPC;
5369 
5370  unsigned int thePlane, theWire;
5371  float theTime;
5372  int dwb, dwe;
5373 
5374  const detinfo::DetectorProperties* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
5375 
5376  for(unsigned short ivx = 0; ivx < vtx3.size(); ++ivx) {
5377  // A complete 3D vertex with matching 2D vertices in all planes?
5378  if(vtx3[ivx].Wire < 0) continue;
5379  if(vtx3[ivx].CStat != cstat || vtx3[ivx].TPC != tpc) continue;
5380  // Find the plane that is missing a 2D vertex
5381  thePlane = 3;
5382  theWire = vtx3[ivx].Wire;
5383  for(plane = 0; plane < 3; ++plane) {
5384  if(vtx3[ivx].Ptr2D[plane] >= 0) continue;
5385  thePlane = plane;
5386  break;
5387  } // plane
5388  if(thePlane > 2) continue;
5389  theTime = detprop->ConvertXToTicks(vtx3[ivx].X, thePlane, tpc, cstat);
5390  clCTP = EncodeCTP(cstat, tpc, thePlane);
5391  // Create a new 2D vertex and see how many clusters we can attach to it
5392  VtxStore vnew;
5393  vnew.Wire = theWire;
5394  vnew.Time = theTime;
5395  vnew.CTP = clCTP;
5396  vnew.Topo = 7;
5397  vnew.Fixed = false;
5398  vtx.push_back(vnew);
5399  unsigned short ivnew = vtx.size() -1;
5400  std::vector<short> vclIndex;
5401  for(unsigned short icl = 0; icl < tcl.size(); ++icl) {
5402  if(tcl[icl].ID < 0) continue;
5403  if(tcl[icl].CTP != clCTP) continue;
5404  dwb = util::absDiff(theWire, tcl[icl].BeginWir);
5405  dwe = util::absDiff(theWire, tcl[icl].EndWir);
5406  // rough cut to start
5407  if(dwb > 10 && dwe > 10) continue;
5408  if(dwb < dwe && dwb < 10 && tcl[icl].BeginVtx < 0) {
5409  // cluster begin is closer
5410  if(theWire < tcl[icl].BeginWir + 5) continue;
5411  if(ClusterVertexChi(icl, 0, ivnew) > fVertex3DCut) continue;
5412  tcl[icl].BeginVtx = ivnew;
5413  vclIndex.push_back(icl);
5414  } else if(dwe < 10 && tcl[icl].EndVtx < 0) {
5415  // cluster end is closer
5416  if(theWire > tcl[icl].EndWir - 5) continue;
5417  if(ClusterVertexChi(icl, 1, ivnew) > fVertex3DCut) continue;
5418  tcl[icl].EndVtx = ivnew;
5419  vclIndex.push_back(icl);
5420  } // dwb/dwe check
5421  } // icl
5422  bool goodVtx = false;
5423  if(vclIndex.size() > 0) {
5424  FitVtx(ivnew);
5425  goodVtx = (vtx[ivnew].ChiDOF < fVertex3DCut);
5426  vtx3[ivx].Ptr2D[thePlane] = ivnew;
5427  }
5428  if(goodVtx) {
5429  vtx3[ivx].Ptr2D[thePlane] = ivnew;
5430  vtx3[ivx].Wire = -1;
5431  } else {
5432  // clobber the vertex
5433  vtx.pop_back();
5434  for(unsigned short ii = 0; ii < vclIndex.size(); ++ii) {
5435  unsigned short icl = vclIndex[ii];
5436  if(tcl[icl].BeginVtx == ivnew) tcl[icl].BeginVtx = -99;
5437  if(tcl[icl].EndVtx == ivnew) tcl[icl].EndVtx = -99;
5438  } // ii
5439  }
5440  } // ivx
5441  } // Vtx3ClusterMatch
5442 
5444  void ClusterCrawlerAlg::Vtx3ClusterSplit(geo::TPCID const& tpcid)
5445  {
5446  // Try to split clusters in a view in which there is no 2D vertex
5447  // assigned to a 3D vertex
5448  if(vtx3.size() == 0) return;
5449  const unsigned int cstat = tpcid.Cryostat;
5450  const unsigned int tpc = tpcid.TPC;
5451 
5452  vtxprt = (fDebugPlane >= 0) && (fDebugHit == 6666);
5453 
5454  unsigned int lastplane = 5, kcl, kclID;
5455  float dth, theTime;
5456  unsigned int thePlane, theWire, plane;
5457  unsigned int loWire, hiWire;
5458 
5459  const detinfo::DetectorProperties* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
5460 
5461  for(unsigned short ivx = 0; ivx < vtx3.size(); ++ivx) {
5462  if(vtx3[ivx].CStat != cstat || vtx3[ivx].TPC != tpc) continue;
5463  // Complete 3D vertex with matching 2D vertices in all planes?
5464  if(vtxprt) mf::LogVerbatim("CC")<<"Vtx3ClusterSplit ivx "<<ivx<<" Ptr2D "<<vtx3[ivx].Ptr2D[0]<<" "<<vtx3[ivx].Ptr2D[1]<<" "<<vtx3[ivx].Ptr2D[2]<<" wire "<<vtx3[ivx].Wire;
5465  if(vtx3[ivx].Wire < 0) continue;
5466  // find the plane that needs to be studied
5467  thePlane = 3;
5468  theWire = vtx3[ivx].Wire;
5469  for(plane = 0; plane < 3; ++plane) {
5470  if(vtx3[ivx].Ptr2D[plane] >= 0) continue;
5471  thePlane = plane;
5472  break;
5473  } // plane
5474  if(thePlane > 2) continue;
5475  theTime = detprop->ConvertXToTicks((double)vtx3[ivx].X,
5476  (int)thePlane, (int)tpcid.TPC, (int)tpcid.Cryostat);
5477  // get the hit range if necessary
5478  if(thePlane != lastplane) {
5479  clCTP = EncodeCTP(tpcid.Cryostat, tpcid.TPC, thePlane);
5480  GetHitRange(clCTP);
5481  lastplane = thePlane;
5482  }
5483  // make a list of clusters that have hits near this point on nearby wires
5484  std::vector<short> clIDs;
5485  if(theWire > fFirstWire + 5) { loWire = theWire - 5; } else { loWire = fFirstWire; }
5486  if(theWire < fLastWire - 5) { hiWire = theWire + 5; } else { hiWire = fLastWire; }
5487  if(vtxprt) mf::LogVerbatim("CC")<<"3DVtx "<<ivx<<" look for cluster hits near P:W:T "<<thePlane<<":"<<theWire<<":"<<(int)theTime<<" Wire range "<<loWire<<" to "<<hiWire;
5488  for(unsigned int wire = loWire; wire < hiWire; ++wire) {
5489  // ignore dead wires or wires with no hits
5490  if(WireHitRange[wire].first < 0) continue;
5491  unsigned int firsthit = WireHitRange[wire].first;
5492  unsigned int lasthit = WireHitRange[wire].second;
5493  for(unsigned int khit = firsthit; khit < lasthit; ++khit) {
5494  // ignore obsolete and un-assigned hits
5495  if(inClus[khit] <= 0) continue;
5496  if((unsigned int)inClus[khit] > tcl.size() + 1) {
5497  mf::LogError("CC")<<"Invalid hit InClus. "<<khit<<" "<<inClus[khit];
5498  continue;
5499  }
5500  // check an expanded time range
5501  if(theTime < fHits[khit].StartTick() - 20) continue;
5502  if(theTime > fHits[khit].EndTick() + 20) continue;
5503  kclID = inClus[khit];
5504  kcl = kclID - 1;
5505  // ignore obsolete clusters
5506  if(tcl[kcl].ID < 0) continue;
5507  // ignore short clusters
5508  if(tcl[kcl].tclhits.size() < 6) continue;
5509  // ignore long straight clusters
5510  if(tcl[kcl].tclhits.size() > 100 && std::abs(tcl[kcl].BeginAng - tcl[kcl].EndAng) < 0.1) continue;
5511  // put the cluster in the list if it's not there already
5512  if(vtxprt) mf::LogVerbatim("CC")<<"Bingo "<<ivx<<" plane "<<thePlane<<" wire "<<wire<<" hit "<<fHits[khit].WireID().Wire<<":"<<(int)fHits[khit].PeakTime()<<" inClus "<<inClus[khit]<<" P:W:T "<<thePlane<<":"<<tcl[kcl].BeginWir<<":"<<(int)tcl[kcl].BeginTim;
5513  if(std::find(clIDs.begin(), clIDs.end(), kclID) == clIDs.end()) {
5514  clIDs.push_back(kclID);
5515  } // std::find
5516  } // khit
5517  } // wire
5518  if(clIDs.size() == 0) continue;
5519  if(vtxprt) for(unsigned int ii = 0; ii < clIDs.size(); ++ii) mf::LogVerbatim("CC")<<" clIDs "<<clIDs[ii];
5520 
5521  unsigned short ii, icl, jj;
5522  unsigned int iht;
5523  short nhitfit;
5524  bool didit;
5525  // find a reasonable time error using the 2D vertices that comprise this
5526  // incomplete 3D vertex
5527  float tErr = 1;
5528  unsigned short i2Dvx = 0;
5529  for(ii = 0; ii < 3; ++ii) {
5530  if(ii == thePlane) continue;
5531  i2Dvx = vtx3[ivx].Ptr2D[ii];
5532  if(i2Dvx > vtx.size() - 1) {
5533  mf::LogError("CC")<<"Vtx3ClusterSplit: Coding error";
5534  return;
5535  }
5536  if(vtx[i2Dvx].TimeErr > tErr) tErr = vtx[i2Dvx].TimeErr;
5537  } // ii -> plane
5538 
5539  // do a local fit near the crossing point and make a tighter cut
5540  for(ii = 0; ii < clIDs.size(); ++ii) {
5541  icl = clIDs[ii] - 1;
5542  didit = false;
5543  for(jj = 0; jj < tcl[icl].tclhits.size(); ++jj) {
5544  iht = tcl[icl].tclhits[jj];
5545  if(fHits[iht].WireID().Wire < theWire) {
5546  nhitfit = 3;
5547  if(jj > 3) nhitfit = -3;
5548  FitClusterMid(icl, iht, nhitfit);
5549  float doca = DoCA(-1, 1, theWire, theTime);
5550  if(vtxprt) mf::LogVerbatim("CC")<<" cls "<<icl<<" dth "<<dth<<" DoCA "<<doca<<" tErr "<<tErr;
5551  if((doca / tErr) > 2) clIDs[ii] = -1;
5552  didit = true;
5553  break;
5554  } // fHits[iht].WireID().Wire < theWire
5555  if(didit) break;
5556  } // jj
5557  if(didit) break;
5558  } // ii
5559  if(vtxprt) {
5560  mf::LogVerbatim("CC")<<"clIDs after fit "<<clIDs.size();
5561  for(ii = 0; ii < clIDs.size(); ++ii) mf::LogVerbatim("CC")<<" clIDs "<<clIDs[ii];
5562  }
5563 
5564  // see if any candidates remain
5565  unsigned short nok = 0;
5566  for(ii = 0; ii < clIDs.size(); ++ii) if(clIDs[ii] >= 0) ++nok;
5567  if(nok == 0) continue;
5568 
5569  // make a new 2D vertex
5570  VtxStore vnew;
5571  vnew.Wire = theWire;
5572  vnew.WireErr = 1;
5573  vnew.Time = theTime;
5574  vnew.TimeErr = 1;
5575  vnew.Topo = 8;
5576  vnew.CTP = clCTP;
5577  vnew.Fixed = false;
5578  vtx.push_back(vnew);
5579  // update the 2D -> 3D vertex pointer
5580  unsigned short ivnew = vtx.size() - 1;
5581  if(vtxprt) mf::LogVerbatim("CC")<<"Make new 2D vtx "<<ivnew<<" in plane "<<thePlane<<" from 3D vtx "<<ivx;
5582  vtx3[ivx].Ptr2D[thePlane] = ivnew;
5583  // either split or attach clusters to this vertex
5584  for(ii = 0; ii < clIDs.size(); ++ii) {
5585  if(clIDs[ii] < 0) continue;
5586  icl = clIDs[ii] - 1;
5587  // find the split position
5588  // split the cluster. Find the split position
5589  unsigned short pos = 0;
5590  for(unsigned short jj = 0; jj < tcl[icl].tclhits.size(); ++jj) {
5591  iht = tcl[icl].tclhits[jj];
5592  if(fHits[iht].WireID().Wire < theWire) {
5593  pos = jj;
5594  break;
5595  }
5596  } // jj
5597  if(pos == 0) {
5598  // vertex is DS of the cluster Begin
5599  tcl[icl].BeginVtx = ivnew;
5600  if(vtxprt) mf::LogVerbatim("CC")<<"Attach to Begin "<<icl;
5601  } else if(pos > tcl[icl].tclhits.size()) {
5602  // vertex is US of the cluster Eend
5603  tcl[icl].EndVtx = ivnew;
5604  if(vtxprt) mf::LogVerbatim("CC")<<"Attach to End "<<icl;
5605  } else {
5606  // vertex is in the middle of the cluster
5607  if(vtxprt) mf::LogVerbatim("CC")<<"Split cluster "<<clIDs[ii]<<" at pos "<<pos;
5608  if(!SplitCluster(icl, pos, ivnew)) {
5609  if(vtxprt) mf::LogVerbatim("CC")<<"SplitCluster failed";
5610  continue;
5611  }
5612  tcl[icl].ProcCode += 10000;
5613  tcl[tcl.size()-1].ProcCode += 10000;
5614  } // pos check
5615  } // ii
5616  // Fit the vertex position
5617  FitVtx(ivnew);
5618  if(vtx[ivnew].ChiDOF < 5 && vtx[ivnew].WireErr < fVertex2DWireErrCut) {
5619  // mark the 3D vertex as complete
5620  vtx3[ivx].Wire = -1;
5621  } else {
5622  if(vtxprt) mf::LogVerbatim("CC")<<"Bad vtx fit "<<ivnew<<" ChiDOF "<<vtx[ivnew].ChiDOF<<" WireErr "<<vtx[ivnew].WireErr;
5623  // Recover (partially) from a bad fit. Leave the ProcCode as-is to trace this problem
5624  vtx.pop_back();
5625  vtx3[ivx].Ptr2D[thePlane] = -1;
5626  // find the cluster - vertex associations
5627  for(jj = 0; jj < tcl.size(); ++jj) {
5628  if(tcl[jj].BeginVtx == ivnew) tcl[jj].BeginVtx = -99;
5629  if(tcl[jj].EndVtx == ivnew) tcl[jj].EndVtx = -99;
5630  } // jj
5631  }
5632  } // ivx
5633 
5634  } // Vtx3ClusterSplit()
5635 
5636 
5638  void ClusterCrawlerAlg::FindHammerClusters()
5639  {
5640  // look for a long cluster that stops at a short cluster in two views. This can occur in a CCmu
5641  // interaction where two protons are emitted back-to-back and are therefore reconstructed as one cluster
5642  // This routine looks for this signature, and if found, splits the short clusters and creates a new 3D vertex.
5643  // This routine only considers the case where the long cluster intersects the short cluster at the US (End) end.
5644 
5645  unsigned int nPln = geom->Cryostat(cstat).TPC(tpc).Nplanes();
5646  if(nPln != 3) return;
5647 
5648  float ew1, ew2, bw2, fvw;
5649 
5650  struct Hammer {
5651  bool Used;
5652  unsigned int Wire; // intersection point of the long cluster and the short cluster
5653  float Tick; // intersection point of the long cluster and the short cluster
5654  float X;
5655  unsigned short longClIndex;
5656  unsigned short shortClIndex;
5657  unsigned short splitPos;
5658  };
5659  std::array< std::vector<Hammer>, 3> hamrVec;
5660 
5661  const detinfo::DetectorProperties* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
5662 
5663  unsigned int ipl;
5664  bool useit = false;
5665  for(ipl = 0; ipl < 3; ++ipl) {
5666  clCTP = EncodeCTP(cstat, tpc, ipl);
5667  for(unsigned short ic1 = 0; ic1 < tcl.size(); ++ic1) {
5668  if(tcl[ic1].ID < 0) continue;
5669  // require a long cluster
5670  if(tcl[ic1].tclhits.size() < 20) continue;
5671  if(tcl[ic1].CTP != clCTP) continue;
5672  // ignore long clusters with an End vertex assignment
5673  if(tcl[ic1].EndVtx >= 0) continue;
5674  ew1 = tcl[ic1].EndWir;
5675  for(unsigned short ic2 = 0; ic2 < tcl.size(); ++ic2) {
5676  if(tcl[ic2].ID < 0) continue;
5677  // require a short cluster
5678  if(tcl[ic2].tclhits.size() > 20) continue;
5679  // but not too short cluster
5680  if(tcl[ic2].tclhits.size() < 6) continue;
5681  if(tcl[ic2].CTP != clCTP) continue;
5682  ew2 = tcl[ic2].EndWir;
5683  bw2 = tcl[ic2].BeginWir;
5684  // check the US end. The End of the long cluster must lie between the Begin and End wire of the
5685  // short cluster
5686  if(ew1 < ew2 || ew1 > bw2 ) continue;
5687  // look for intersection of the two clusters
5688  float best = 10;
5689  short ibst = -1;
5690  unsigned short spos = 0;
5691  for(unsigned short ii = 0; ii < tcl[ic2].tclhits.size(); ++ii) {
5692  unsigned int iht = tcl[ic2].tclhits[ii];
5693  float dw = fHits[iht].WireID().Wire - tcl[ic1].EndWir;
5694  float dt = fabs(fHits[iht].PeakTime() - tcl[ic1].EndTim - tcl[ic1].EndSlp * dw);
5695  if(dt < best) {
5696  best = dt;
5697  ibst = iht;
5698  spos = ii;
5699  }
5700  } // ii
5701  if(ibst < 0) continue;
5702  fvw = 0.5 + fHits[ibst].WireID().Wire;
5703  Hammer aHam;
5704  aHam.Used = false;
5705  aHam.Wire = (0.5 + fvw);
5706  aHam.Tick = fHits[ibst].PeakTime();
5707  aHam.X = detprop->ConvertTicksToX((double)aHam.Tick, (int)ipl, (int)tpc, (int)cstat);
5708 // std::cout<<"aHam "<<" fvw "<<fvw<<" X "<<aHam.X<<" ic1 ID "<<tcl[ic1].ID<<" ic2 ID "<<tcl[ic2].ID<<"\n";
5709  aHam.longClIndex = ic1;
5710  aHam.shortClIndex = ic2;
5711  aHam.splitPos = spos;
5712  unsigned short indx = hamrVec[ipl].size();
5713  hamrVec[ipl].resize(indx + 1);
5714  hamrVec[ipl][indx] = aHam;
5715 // hamrVec[ipl].push_back(aHam);
5716  useit = true;
5717  } // ic2
5718  if(useit) break;
5719  } // ic1
5720  } // ipl
5721 
5722  unsigned short noham = 0;
5723  for(ipl = 0; ipl < 3; ++ipl) if(hamrVec[ipl].size() == 0) ++noham;
5724  if(noham > 1) return;
5725 
5726  // Y,Z limits of the detector
5727  double local[3] = {0.,0.,0.};
5728  double world[3] = {0.,0.,0.};
5729 
5730  const geo::TPCGeo &thetpc = geom->TPC(tpc, cstat);
5731  thetpc.LocalToWorld(local,world);
5732  float YLo = world[1]-geom->DetHalfHeight(tpc,cstat) + 1;
5733  float YHi = world[1]+geom->DetHalfHeight(tpc,cstat) - 1;
5734  float ZLo = world[2]-geom->DetLength(tpc,cstat)/2 + 1;
5735  float ZHi = world[2]+geom->DetLength(tpc,cstat)/2 - 1;
5736 
5737  // Match in 3D
5738  float dX;
5739  double y, z;
5740  unsigned short icl, jpl, jcl, kpl, splitPos;
5741  for(ipl = 0; ipl < 3; ++ipl) {
5742  if(hamrVec[ipl].size() == 0) continue;
5743  jpl = (ipl + 1) % nPln;
5744  kpl = (jpl + 1) % nPln;
5745  for(unsigned short ii = 0; ii < hamrVec[ipl].size(); ++ii) {
5746  if(hamrVec[ipl][ii].Used) continue;
5747  for(unsigned short jj = 0; jj < hamrVec[jpl].size(); ++jj) {
5748  if(hamrVec[jpl][jj].Used) continue;
5749  dX = hamrVec[ipl][ii].X - hamrVec[jpl][jj].X;
5750  if(fabs(dX) > fVertex3DCut) continue;
5751  geom->IntersectionPoint(hamrVec[ipl][ii].Wire, hamrVec[jpl][jj].Wire, ipl, jpl, cstat, tpc, y, z);
5752  if(y < YLo || y > YHi || z < ZLo || z > ZHi) continue;
5753  // mark them used
5754  hamrVec[ipl][ii].Used = true;
5755  hamrVec[jpl][jj].Used = true;
5756  // make a new 3D vertex
5757  Vtx3Store newVtx3;
5758  newVtx3.ProcCode = 7;
5759  newVtx3.X = 0.5 * (hamrVec[ipl][ii].X + hamrVec[jpl][jj].X);
5760  // TODO: do this correctly;
5761  newVtx3.XErr = fabs(hamrVec[ipl][ii].X - hamrVec[jpl][jj].X);
5762  newVtx3.Y = y;
5763  newVtx3.YErr = 1; // TODO
5764  newVtx3.Z = z;
5765  newVtx3.ZErr = 1; // TODO
5766  newVtx3.CStat = cstat;
5767  newVtx3.TPC = tpc;
5768 
5769  // make 2D vertex in ipl
5770  VtxStore newVtx2;
5771  newVtx2.Wire = hamrVec[ipl][ii].Wire;
5772  newVtx2.WireErr = 2;
5773  newVtx2.Time = hamrVec[ipl][ii].Tick;
5774  newVtx2.TimeErr = 5;
5775  newVtx2.Topo = 6;
5776  newVtx2.Fixed = false;
5777  icl = hamrVec[ipl][ii].longClIndex;
5778  newVtx2.CTP = tcl[icl].CTP;
5779  vtx.push_back(newVtx2);
5780  unsigned short ivnew = vtx.size() - 1;
5781  // associate the new vertex with the long cluster
5782  tcl[icl].EndVtx = ivnew;
5783  FitVtx(ivnew);
5784  // stash the index in newVtx3
5785  newVtx3.Ptr2D[ipl] = (short)ivnew;
5786  // split the short cluster and associate the new clusters with the new vtx
5787  icl = hamrVec[ipl][ii].shortClIndex;
5788  splitPos = hamrVec[ipl][ii].splitPos;
5789  if(!SplitCluster(icl, splitPos, ivnew)) return;
5790 
5791  // make 2D vertex in jpl
5792  newVtx2.Wire = hamrVec[jpl][jj].Wire;
5793  newVtx2.Time = hamrVec[jpl][jj].Tick;
5794  newVtx2.Topo = 6;
5795  jcl = hamrVec[jpl][jj].longClIndex;
5796  newVtx2.CTP = tcl[jcl].CTP;
5797  vtx.push_back(newVtx2);
5798  ivnew = vtx.size() - 1;
5799 // std::cout<<"newVtx2 jpl index "<<vtx.size()-1<<" CTP "<<newVtx2.CTP<<"\n";
5800  // associate the new vertex with the long cluster
5801  tcl[jcl].EndVtx = ivnew;
5802  // stash the index in newVtx3
5803  newVtx3.Ptr2D[jpl] = (short)(vtx.size() - 1);
5804  // split the short cluster and associate the new clusters with the new vtx
5805  jcl = hamrVec[jpl][jj].shortClIndex;
5806  splitPos = hamrVec[jpl][jj].splitPos;
5807  if(!SplitCluster(jcl, splitPos, vtx.size() - 1)) return;
5808  FitVtx(ivnew);
5809 // std::cout<<"Split jpl "<<jpl<<" cl ID "<<tcl[jcl].ID<<" pos "<<splitPos<<" vtx "<<vtx.size()-1<<"\n";
5810 
5811  // set the kpl 2D vertex index < 0. Let follow-on code find the 3rd plane vertex
5812  newVtx3.Ptr2D[kpl] = -1;
5813  double WPos[3] = {0, y, z};
5814  try{
5815  newVtx3.Wire = geom->NearestWire(WPos, kpl, tpc, cstat);
5816  }
5817  catch(geo::InvalidWireError const& e) {
5818  newVtx3.Wire = e.suggestedWireID().Wire; // pick the closest valid wire
5819  }
5820 // std::cout<<"3D Vtx "<<ipl<<":"<<ii<<" "<<jpl<<":"<<jj<<" kpl "<<kpl<<" wire "<<newVtx3.Wire<<"\n";
5821  vtx3.push_back(newVtx3);
5822  } // jj
5823  } // ii
5824  }
5825 
5826  } // FindHammerClusters
5827 
5828 
5830  void ClusterCrawlerAlg::VtxMatch(geo::TPCID const& tpcid)
5831  {
5832  // Create 3D vertices from 2D vertices. 3D vertices that are matched
5833  // in all three planes have Ptr2D >= 0 for all planes
5834 
5835 
5836  geo::TPCGeo const& TPC = geom->TPC(tpcid);
5837  const detinfo::DetectorProperties* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
5838 
5839  const unsigned int cstat = tpcid.Cryostat;
5840  const unsigned int tpc = tpcid.TPC;
5841 
5842  // Y,Z limits of the detector
5843  double local[3] = {0.,0.,0.};
5844  double world[3] = {0.,0.,0.};
5845 
5846  const geo::TPCGeo &thetpc = geom->TPC(tpc, cstat);
5847  thetpc.LocalToWorld(local,world);
5848  // reduce the active area of the TPC by 1 cm to prevent wire boundary issues
5849  float YLo = world[1]-geom->DetHalfHeight(tpc,cstat) + 1;
5850  float YHi = world[1]+geom->DetHalfHeight(tpc,cstat) - 1;
5851  float ZLo = world[2]-geom->DetLength(tpc,cstat)/2 + 1;
5852  float ZHi = world[2]+geom->DetLength(tpc,cstat)/2 - 1;
5853 
5854  vtxprt = (fDebugPlane >= 0) && (fDebugHit == 6666);
5855 
5856  if(vtxprt) {
5857  mf::LogVerbatim("CC")<<"Inside VtxMatch";
5858  PrintVertices();
5859  }
5860 
5861  // wire spacing in cm
5862  float wirePitch = geom->WirePitch(0, tpcid.TPC, tpcid.Cryostat);
5863 
5864  // fill temp vectors of 2D vertex X and X errors
5865  std::vector<float> vX(vtx.size());
5866  std::vector<float> vXsigma(vtx.size());
5867  float vXp;
5868  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
5869  if(vtx[ivx].NClusters == 0) continue;
5870  geo::PlaneID iplID = DecodeCTP(vtx[ivx].CTP);
5871  if(iplID.TPC != tpc || iplID.Cryostat != cstat) continue;
5872  // Convert 2D vertex time error to X error
5873  vX[ivx] = detprop->ConvertTicksToX((double)vtx[ivx].Time, (int)iplID.Plane, (int)tpc, (int)cstat);
5874  vXp = detprop->ConvertTicksToX((double)(vtx[ivx].Time + vtx[ivx].TimeErr), (int)iplID.Plane, (int)tpc, (int)cstat);
5875  vXsigma[ivx] = fabs(vXp - vX[ivx]);
5876  } // ivx
5877 
5878  // create a array/vector of 2D vertex indices in each plane
5879  std::array<std::vector<unsigned short>, 3> vIndex;
5880  unsigned short indx, ipl;
5881  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
5882  if(vtx[ivx].NClusters == 0) continue;
5883  geo::PlaneID iplID = DecodeCTP(vtx[ivx].CTP);
5884  if(iplID.TPC != tpc || iplID.Cryostat != cstat) continue;
5885  ipl = iplID.Plane;
5886  if(ipl > 2) continue;
5887  indx = vIndex[ipl].size();
5888  vIndex[ipl].resize(indx + 1);
5889  vIndex[ipl][indx] = ivx;
5890  }
5891 
5892  // vector of 2D vertices -> 3D vertices.
5893  std::vector<short> vPtr;
5894  for(unsigned short ii = 0; ii < vtx.size(); ++ii) vPtr.push_back(-1);
5895 
5896  // temp vector of all 2D vertex matches
5897  std::vector<Vtx3Store> v3temp;
5898 
5899  double y = 0, z = 0;
5900  TVector3 WPos = {0, 0, 0};
5901  // i, j, k indicates 3 different wire planes
5902  unsigned short ii, jpl, jj, kpl, kk, ivx, jvx, kvx;
5903  unsigned int iWire, jWire;
5904  unsigned short v3dBest = 0;
5905  float xbest = 0, ybest = 0, zbest = 0;
5906  float kX, kWire;
5907  // compare vertices in each view
5908  bool gotit = false;
5909  for(ipl = 0; ipl < 2; ++ipl) {
5910  for(ii = 0; ii < vIndex[ipl].size(); ++ii) {
5911  ivx = vIndex[ipl][ii];
5912  if(ivx > vtx.size() - 1) {
5913  mf::LogError("CC")<<"VtxMatch: bad ivx "<<ivx;
5914  return;
5915  }
5916  // vertex has been matched already
5917  if(vPtr[ivx] >= 0) continue;
5918  iWire = vtx[ivx].Wire;
5919  float best = fVertex3DCut;
5920  // temp array of 2D vertex indices in each plane
5921  // BUG the double brace syntax is required to work around clang bug 21629
5922  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
5923  std::array<short, 3> t2dIndex = {{-1, -1, -1}};
5924  std::array<short, 3> tmpIndex = {{-1, -1, -1}};
5925  for(jpl = ipl + 1; jpl < 3; ++jpl) {
5926  for(jj = 0; jj < vIndex[jpl].size(); ++jj) {
5927  jvx = vIndex[jpl][jj];
5928  if(jvx > vtx.size() - 1) {
5929  mf::LogError("CC")<<"VtxMatch: bad jvx "<<jvx;
5930  return;
5931  }
5932  // vertex has been matched already
5933  if(vPtr[jvx] >= 0) continue;
5934  jWire = vtx[jvx].Wire;
5935  // new stuff
5936  float dX = fabs(vX[ivx] - vX[jvx]);
5937  float dXSigma = sqrt(vXsigma[ivx] * vXsigma[ivx] + vXsigma[jvx] * vXsigma[jvx]);
5938  float dXChi = dX / dXSigma;
5939 
5940  if(vtxprt) mf::LogVerbatim("CC")<<"VtxMatch: ipl "<<ipl<<" ivx "<<ivx<<" ivX "<<vX[ivx]
5941  <<" jpl "<<jpl<<" jvx "<<jvx<<" jvX "<<vX[jvx]<<" W:T "<<(int)vtx[jvx].Wire<<":"<<(int)vtx[jvx].Time<<" dXChi "<<dXChi<<" fVertex3DCut "<<fVertex3DCut;
5942 
5943  if(dXChi > fVertex3DCut) continue;
5944  geom->IntersectionPoint(iWire, jWire, ipl, jpl, cstat, tpc, y, z);
5945  if(y < YLo || y > YHi || z < ZLo || z > ZHi) continue;
5946  WPos[1] = y;
5947  WPos[2] = z;
5948  kpl = 3 - ipl - jpl;
5949  kX = 0.5 * (vX[ivx] + vX[jvx]);
5950  kWire = -1;
5951  if(TPC.Nplanes() > 2){
5952  try{
5953  kWire = geom->NearestWire(WPos, kpl, tpc, cstat);
5954  }
5955  catch(geo::InvalidWireError const& e) {
5956  kWire = e.suggestedWireID().Wire; // pick the closest valid wire
5957  }
5958  }
5959  kpl = 3 - ipl - jpl;
5960  // save this incomplete 3D vertex
5961  Vtx3Store v3d;
5962  v3d.ProcCode = 1;
5963  tmpIndex[ipl] = ivx;
5964  tmpIndex[jpl] = jvx;
5965  tmpIndex[kpl] = -1;
5966  v3d.Ptr2D = tmpIndex;
5967  v3d.X = kX;
5968  v3d.XErr = dXSigma;
5969  v3d.Y = y;
5970  float yzSigma = wirePitch * sqrt(vtx[ivx].WireErr * vtx[ivx].WireErr + vtx[jvx].WireErr * vtx[jvx].WireErr);
5971  v3d.YErr = yzSigma;
5972  v3d.Z = z;
5973  v3d.ZErr = yzSigma;
5974  v3d.Wire = kWire;
5975  v3d.CStat = cstat;
5976  v3d.TPC = tpc;
5977  v3temp.push_back(v3d);
5978 
5979  if(vtxprt) mf::LogVerbatim("CC")
5980  <<"VtxMatch: 2 Plane match ivx "<<ivx<<" P:W:T "<<ipl<<":"<<(int)vtx[ivx].Wire<<":"<<(int)vtx[ivx].Time
5981  <<" jvx "<<jvx<<" P:W:T "<<jpl<<":"<<(int)vtx[jvx].Wire<<":"<<(int)vtx[jvx].Time<<" dXChi "<<dXChi<<" yzSigma "<<yzSigma;
5982 
5983  if(TPC.Nplanes() == 2) continue;
5984  // look for a 3 plane match
5985  best = fVertex3DCut;
5986  for(kk = 0; kk < vIndex[kpl].size(); ++kk) {
5987  kvx = vIndex[kpl][kk];
5988  if(vPtr[kvx] >= 0) continue;
5989 // float kvxX = detprop->ConvertTicksToX((double)vtx[kvx].Time, (int)kpl, (int)tpc, (int)cstat);
5990  // Wire difference error
5991  float dW = wirePitch * (vtx[kvx].Wire - kWire) / yzSigma;
5992  // X difference error
5993  float dX = (vX[kvx] - kX) / dXSigma;
5994  float kChi = 0.5 * sqrt(dW * dW + dX * dX);
5995  if(kChi < best) {
5996  best = kChi;
5997  xbest = (vX[kvx] + 2 * kX) / 3;
5998  ybest = y;
5999  zbest = z;
6000  t2dIndex[ipl] = ivx;
6001  t2dIndex[jpl] = jvx;
6002  t2dIndex[kpl] = kvx;
6003  v3dBest = v3temp.size() - 1;
6004  }
6005 
6006  if(vtxprt) mf::LogVerbatim("CC")<<" kvx "<<kvx<<" kpl "<<kpl
6007  <<" wire "<<(int)vtx[kvx].Wire<<" kTime "<<(int)vtx[kvx].Time<<" kChi "<<kChi<<" best "<<best<<" dW "<<vtx[kvx].Wire - kWire;
6008 
6009  } // kk
6010  if(vtxprt) mf::LogVerbatim("CC")<<" done best = "<<best<<" fVertex3DCut "<<fVertex3DCut;
6011  if(TPC.Nplanes() > 2 && best < fVertex3DCut) {
6012  // create a real 3D vertex using the previously entered incomplete 3D vertex as a template
6013  if(v3dBest > v3temp.size() - 1) {
6014  mf::LogError("CC")<<"VtxMatch: bad v3dBest "<<v3dBest;
6015  return;
6016  }
6017  Vtx3Store v3d = v3temp[v3dBest];
6018  v3d.Ptr2D = t2dIndex;
6019  v3d.Wire = -1;
6020  // TODO need to average ybest and zbest here with error weighting
6021  v3d.X = xbest;
6022  v3d.Y = ybest;
6023  v3d.Z = zbest;
6024  vtx3.push_back(v3d);
6025  gotit = true;
6026  // mark the 2D vertices as used
6027  for(unsigned short jj = 0; jj < 3; ++jj) if(t2dIndex[jj] >= 0) vPtr[t2dIndex[jj]] = vtx3.size() - 1;
6028 
6029  if(vtxprt) mf::LogVerbatim("CC")<<"New 3D vtx "<<vtx3.size()<<" X "<<v3d.X<<" Y "<<v3d.Y<<" Z "<<v3d.Z
6030  <<" t2dIndex "<<t2dIndex[0]<<" "<<t2dIndex[1]<<" "<<t2dIndex[2]<<" best Chi "<<best;
6031 
6032  } // best < dRCut
6033  if(gotit) break;
6034  } // jj
6035  if(gotit) break;
6036  } // jpl
6037  if(gotit) break;
6038  } // ii
6039  } // ipl
6040 
6041  // Store incomplete 3D vertices but ignore those that are part of a complete 3D vertex
6042  unsigned short vsize = vtx3.size();
6043  for(unsigned short it = 0; it < v3temp.size(); ++it) {
6044  bool keepit = true;
6045  for(unsigned short i3d = 0; i3d < vsize; ++i3d) {
6046  for(unsigned short plane = 0; plane < 3; ++plane) {
6047  if(v3temp[it].Ptr2D[plane] == vtx3[i3d].Ptr2D[plane]) {
6048  keepit = false;
6049  break;
6050  }
6051  } // plane
6052  if(!keepit) break;
6053  } // i3d
6054 
6055  if(keepit) vtx3.push_back(v3temp[it]);
6056  } // it
6057 
6058  // Modify Ptr2D for 2-plane detector
6059  if(TPC.Nplanes() == 2) {
6060  for(unsigned short iv3 = 0; iv3 < vtx3.size(); ++iv3) {
6061  vtx3[iv3].Ptr2D[2] = 666;
6062  } //iv3
6063  } // 2 planes
6064 
6065  if(vtxprt) {
6066  for(unsigned short it = 0; it < vtx3.size(); ++it) {
6067  mf::LogVerbatim("CC")<<"vtx3 "<<it<<" Ptr2D "<<vtx3[it].Ptr2D[0]<<" "<<vtx3[it].Ptr2D[1]<<" "<<vtx3[it].Ptr2D[2]
6068  <<" wire "<<vtx3[it].Wire;
6069  }
6070  }
6071 
6072  } // VtxMatch
6073 
6075  void ClusterCrawlerAlg::GetHitRange(CTP_t CTP)
6076  {
6077  // fills the WireHitRange vector for the supplied Cryostat/TPC/Plane code
6078  // Hits must have been sorted by increasing wire number
6079  fFirstHit = 0;
6080  geo::PlaneID planeID = DecodeCTP(CTP);
6081  unsigned int nwires = geom->Nwires(planeID.Plane, planeID.TPC, planeID.Cryostat);
6082  WireHitRange.resize(nwires + 1);
6083 
6084  // These will be re-defined later
6085  fFirstWire = 0;
6086  fLastWire = 0;
6087 
6088  unsigned int wire, iht;
6089  unsigned int nHitInPlane;
6090  std::pair<int, int> flag;
6091 
6092  // Define the "no hits on wire" condition
6093  flag.first = -2; flag.second = -2;
6094  for(auto& apair : WireHitRange) apair = flag;
6095 
6096  nHitInPlane = 0;
6097 
6098  std::vector<bool> firsthit;
6099  firsthit.resize(nwires+1, true);
6100  bool firstwire = true;
6101  for(iht = 0; iht < fHits.size(); ++iht) {
6102  if(fHits[iht].WireID().TPC != planeID.TPC) continue;
6103  if(fHits[iht].WireID().Cryostat != planeID.Cryostat) continue;
6104  if(fHits[iht].WireID().Plane != planeID.Plane) continue;
6105  wire = fHits[iht].WireID().Wire;
6106  // define the first hit start index in this TPC, Plane
6107  if(firsthit[wire]) {
6108  WireHitRange[wire].first = iht;
6109  firsthit[wire] = false;
6110  }
6111  if(firstwire){
6112  fFirstWire = wire;
6113  firstwire = false;
6114  }
6115  WireHitRange[wire].second = iht+1;
6116  fLastWire = wire+1;
6117  ++nHitInPlane;
6118  }
6119  // overwrite with the "dead wires" condition
6120  lariov::ChannelStatusProvider const& channelStatus
6122 
6123  flag.first = -1; flag.second = -1;
6124  unsigned int nbad = 0;
6125  for(wire = 0; wire < nwires; ++wire) {
6126  raw::ChannelID_t chan = geom->PlaneWireToChannel((int)planeID.Plane,(int)wire,(int)planeID.TPC,(int)planeID.Cryostat);
6127  if(!channelStatus.IsGood(chan)) {
6128  WireHitRange[wire] = flag;
6129  ++nbad;
6130  }
6131  } // wire
6132 // std::cout<<nbad<<" bad wires in plane "<<planeID.Plane<<"\n";
6133 
6134  // define the MergeAvailable vector and check for errors
6135  if(mergeAvailable.size() < fHits.size()) throw art::Exception(art::errors::LogicError)
6136  <<"GetHitRange: Invalid mergeAvailable vector size "<<mergeAvailable.size()<<fHits.size();
6137  unsigned int firstHit, lastHit;
6138  unsigned int cnt;
6139  cnt = 0;
6140  float maxRMS, chiSep, peakCut;
6141  for(wire = 0; wire < nwires; ++wire) {
6142  // ignore dead wires and wires with no hits
6143  if(WireHitRange[wire].first < 0) continue;
6144  firstHit = WireHitRange[wire].first;
6145  lastHit = WireHitRange[wire].second;
6146  for(iht = firstHit; iht < lastHit; ++iht) {
6147  if(fHits[iht].WireID().Wire != wire)
6148  throw art::Exception(art::errors::LogicError)<<"Bad WireHitRange on wire "<<wire<<"\n";
6149  ++cnt;
6150  if(fHits[iht].Multiplicity() > 1) {
6151  peakCut = 0.6 * fHits[iht].PeakAmplitude();
6152  std::pair<size_t, size_t> MultipletRange = FindHitMultiplet(iht);
6153  for(size_t jht = MultipletRange.first; jht < MultipletRange.second; ++jht) {
6154  if(jht == iht) continue;
6155  // require that the j hit be similar in magnitude to the i hit
6156  if(fHits[jht].PeakAmplitude() < peakCut) continue;
6157  maxRMS = std::max(fHits[iht].RMS(), fHits[jht].RMS());
6158  chiSep = std::abs(fHits[iht].PeakTime() - fHits[jht].PeakTime()) / maxRMS;
6159  if(chiSep < fHitMergeChiCut) {
6160  mergeAvailable[iht] = true;
6161  break;
6162  }
6163  } // jht
6164  } // fHits[iht].Multiplicity() > 1
6165  } // iht
6166  } // wire
6167  if(cnt != nHitInPlane) mf::LogWarning("CC")<<"Bad WireHitRange count "<<cnt<<" "<<nHitInPlane<<"\n";
6168 
6169  if(!fMergeAllHits) return;
6170 
6171  // Merge all of the hits
6172  bool didMerge;
6173  for(wire = 0; wire < nwires; ++wire) {
6174  if(WireHitRange[wire].first < 0) continue;
6175  firstHit = WireHitRange[wire].first;
6176  lastHit = WireHitRange[wire].second;
6177  for(iht = firstHit; iht < lastHit; ++iht) {
6178  if(!mergeAvailable[iht]) continue;
6179  // already merged?
6180  if(fHits[iht].GoodnessOfFit() == 6666) continue;
6181  MergeHits(iht, didMerge);
6182  mergeAvailable[iht] = false;
6183  } // iht
6184  } // wire
6185 
6186  } // GetHitRange()
6187 
6189  unsigned int ClusterCrawlerAlg::DeadWireCount(unsigned int inWire1, unsigned int inWire2)
6190  {
6191  if(inWire1 > inWire2) {
6192  // put in increasing order
6193  unsigned int tmp = inWire1;
6194  inWire1 = inWire2;
6195  inWire2 = tmp;
6196  } // inWire1 > inWire2
6197  ++inWire2;
6198  unsigned int wire, ndead = 0;
6199  for(wire = inWire1; wire < inWire2; ++wire) if(WireHitRange[wire].first == -1) ++ndead;
6200  return ndead;
6201  } // DeadWireCount
6202 
6205  {
6206  // Counts the number of dead wires in the range spanned by fcl2hits
6207  if(fcl2hits.size() < 2) return 0;
6208  unsigned int wire, ndead = 0;
6209  unsigned int iht = fcl2hits[fcl2hits.size()-1];
6210  unsigned int eWire = fHits[iht].WireID().Wire;
6211  iht = fcl2hits[0];
6212  unsigned int bWire = fHits[iht].WireID().Wire + 1;
6213  for(wire = eWire; wire < bWire; ++wire) if(WireHitRange[wire].first == -1) ++ndead;
6214  return ndead;
6215  } // DeadWireCount
6216 
6218  bool ClusterCrawlerAlg::areInSameMultiplet
6219  (recob::Hit const& first_hit, recob::Hit const& second_hit)
6220  {
6221  return (first_hit.StartTick() == second_hit.StartTick())
6222  && (first_hit.WireID() == second_hit.WireID());
6223  } // ClusterCrawlerAlg::areInSameMultiplet()
6224 
6225 
6227  std::pair<size_t, size_t> ClusterCrawlerAlg::FindHitMultiplet(size_t iHit) const
6228  {
6229  std::pair<size_t, size_t> range{ iHit, iHit };
6230 
6231  range.first = iHit - fHits[iHit].LocalIndex();
6232  range.second = range.first + fHits[iHit].Multiplicity();
6233 
6234  return range;
6235 /*
6236  const raw::TDCtick_t MultipletStartTime = fHits[iHit].StartTick();
6237  geo::WireID const& wid = fHits[iHit].WireID();
6238 
6239  // detect the first hit with the same start time as the reference hit
6240  while (range.first > 0) {
6241  if (fHits[range.first - 1].StartTick() != MultipletStartTime) break;
6242  if (fHits[range.first - 1].WireID() != wid) break;
6243  --range.first;
6244  } // while
6245 
6246  // detect the last hit with the same start time as the reference hit
6247  while (++range.second < fHits.size()) {
6248  if (fHits[range.second].StartTick() != MultipletStartTime) break;
6249  if (fHits[range.second].WireID() != wid) break;
6250  } // while
6251  return range;
6252  */
6253  } // ClusterCrawlerAlg::FindHitMultiplet()
6254 
6256  bool ClusterCrawlerAlg::CheckHitDuplicates
6257  (std::string location, std::string marker /* = "" */) const
6258  {
6259  // currently unused, only for debug
6260  unsigned int nDuplicates = 0;
6261  std::set<unsigned int> hits;
6262  for (unsigned int hit: fcl2hits) {
6263  if (hits.count(hit)) {
6264  ++nDuplicates;
6265  mf::LogProblem log("CC");
6266  log << "Hit #" << hit
6267  << " being included twice in the future cluster (ID="
6268  << (tcl.size() + 1) << "?) at location: " << location;
6269  if (!marker.empty()) log << " (marker: '" << marker << "')";
6270  }
6271  hits.insert(hit);
6272  } // for
6273  return nDuplicates > 0;
6274  } // ClusterCrawlerAlg::CheckHitDuplicates()
6275 
6277  float ClusterCrawlerAlg::DoCA(short icl, unsigned short end, float vwire, float vtick)
6278  {
6279  // Find the Distance of Closest Approach betweeen a cluster and a point (vwire, vtick). The
6280  // DoCA is returned in Tick units.
6281 
6282  if(icl > (short)tcl.size()) return 9999;
6283 
6284  float cwire, cslp, ctick;
6285  // figure out which cluster to use
6286  if(icl < 0) {
6287  if(fcl2hits.size() == 0) return 9999;
6288  // cluster under construction
6289  if(end == 0) {
6290  cwire = clBeginWir;
6291  cslp = clBeginSlp;
6292  ctick = clBeginTim;
6293  } else {
6294  cwire = clpar[2];
6295  cslp = clpar[1];
6296  ctick = clpar[0];
6297  } // end
6298  } else {
6299  // tcl cluster
6300  if(end == 0) {
6301  cwire = tcl[icl].BeginWir;
6302  cslp = tcl[icl].BeginSlp;
6303  ctick = tcl[icl].BeginTim;
6304  } else {
6305  cwire = tcl[icl].EndWir;
6306  cslp = tcl[icl].EndSlp;
6307  ctick = tcl[icl].EndTim;
6308  } // end
6309  }
6310 
6311  // Closest approach wire
6312  float docaW = (vwire + cslp * (vtick - ctick) + cwire * cslp * cslp) / (1 + cslp * cslp);
6313  float dW = docaW - vwire;
6314  float dT = ctick + (vwire - cwire) * cslp - vtick;
6315  return sqrt(dW * dW + dT * dT);
6316 
6317  } // DoCA
6318 
6320  float ClusterCrawlerAlg::ClusterVertexChi(short icl, unsigned short end, unsigned short ivx)
6321  {
6322  // Returns the chisq/DOF between a cluster and a vertex
6323 
6324  if(icl > (short)tcl.size()) return 9999;
6325  if(ivx > vtx.size()) return 9999;
6326 
6327  float cwire, cslp, cslpErr, ctick;
6328  // figure out which cluster to use
6329  if(icl < 0) {
6330  if(fcl2hits.size() == 0) return 9999;
6331  // cluster under construction
6332  if(end == 0) {
6333  cwire = clBeginWir;
6334  cslp = clBeginSlp;
6335  cslpErr = clBeginSlpErr;
6336  ctick = clBeginTim;
6337  } else {
6338  cwire = clpar[2];
6339  cslp = clpar[1];
6340  cslpErr = clparerr[1];
6341  ctick = clpar[0];
6342  } // end
6343  } else {
6344  // tcl cluster
6345  if(end == 0) {
6346  cwire = tcl[icl].BeginWir;
6347  cslp = tcl[icl].BeginSlp;
6348  cslpErr = tcl[icl].BeginSlpErr;
6349  ctick = tcl[icl].BeginTim;
6350  } else {
6351  cwire = tcl[icl].EndWir;
6352  cslp = tcl[icl].EndSlp;
6353  cslpErr = tcl[icl].EndSlpErr;
6354  ctick = tcl[icl].EndTim;
6355  } // end
6356  }
6357 
6358  // Closest approach wire
6359  float docaW = (vtx[ivx].Wire + cslp * (vtx[ivx].Time - ctick) + cwire * cslp * cslp) / (1 + cslp * cslp);
6360  float dW = docaW - vtx[ivx].Wire;
6361  float chi = dW / vtx[ivx].WireErr;
6362  float totChi = chi * chi;
6363  dW = vtx[ivx].Wire - cwire;
6364  float dT = ctick + dW * cslp - vtx[ivx].Time;
6365  if(cslpErr < 1E-3) cslpErr = 1E-3;
6366  // increase slope error for large angle clusters
6367  cslpErr *= AngleFactor(cslp);
6368  // cluster slope projection error
6369  float dTErr = dW * cslpErr;
6370  // squared
6371  dTErr *= dTErr;
6372  // add the vertex time error^2 to the cluster projection error^2
6373  dTErr += vtx[ivx].TimeErr * vtx[ivx].TimeErr;
6374  if(dTErr < 1E-3) dTErr = 1E-3;
6375  totChi += dT * dT / dTErr;
6376  totChi /= 2;
6377 
6378  return totChi;
6379 
6380  } // ClusterVertexChi
6381 
6383  float ClusterCrawlerAlg::PointVertexChi(float wire, float tick, unsigned short ivx)
6384  {
6385  // Returns the Chisq/DOF between a (Wire, Tick) point and a vertex
6386 
6387  if(ivx > vtx.size()) return 9999;
6388 
6389  float dW = wire - vtx[ivx].Wire;
6390  float chi = dW / vtx[ivx].WireErr;
6391  float totChi = chi * chi;
6392  float dT = tick - vtx[ivx].Time;
6393  chi = dT / vtx[ivx].TimeErr;
6394  totChi += chi * chi;
6395 
6396  return totChi;
6397 
6398  } // PointVertexChi
6399 
6400 
6402  std::string ClusterCrawlerAlg::PrintHit(unsigned int iht)
6403  {
6404 
6405  if(iht > fHits.size() - 1) return "Bad Hit";
6406  return std::to_string(fHits[iht].WireID().Plane) + ":" +std::to_string(fHits[iht].WireID().Wire) + ":" + std::to_string((int)fHits[iht].PeakTime());
6407 
6408  } // PrintHit
6409 
6410 
6411 } // namespace cluster
Float_t x
Definition: compare.C:6
short int LocalIndex() const
How well do we believe we know this hit?
Definition: Hit.h:228
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
Functions to help with numbers.
geo::SigType_t SignalType() const
Signal type for the plane of the hit.
Definition: Hit.h:232
void MergeOverlap(std::string inFcnLabel, TjStuff &tjs, const CTP_t &inCTP, bool prt)
Definition: TCShower.cxx:2530
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
static unsigned int kWire
Float_t E
Definition: plot.C:23
struct of temporary 2D vertices (end points)
geo::WireID WireID() const
Initial tdc tick for hit.
Definition: Hit.h:234
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:145
float RMS() const
RMS of the hit shape, in tick units.
Definition: Hit.h:221
Declaration of signal hit object.
Float_t y
Definition: compare.C:6
void PrintClusters()
Double_t z
Definition: plot.C:279
Planes which measure X direction.
Definition: geo_types.h:81
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
Geometry information for a single TPC.
Definition: TPCGeo.h:37
float SigmaPeakAmplitude() const
Uncertainty on estimated amplitude of the hit at its peak, in ADC units.
Definition: Hit.h:223
float SigmaIntegral() const
Initial tdc tick for hit.
Definition: Hit.h:226
int DegreesOfFreedom() const
Initial tdc tick for hit.
Definition: Hit.h:230
virtual double SamplingRate() const =0
Returns the period of the TPC readout electronics clock.
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
float Integral() const
Integral under the calibrated signal waveform of the hit, in tick x ADC units.
Definition: Hit.h:225
geo::View_t View() const
View for the plane of the hit.
Definition: Hit.h:233
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:313
Float_t tmp
Definition: plot.C:37
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
Cluster finding and building.
float GoodnessOfFit() const
Degrees of freedom in the determination of the hit signal shape (-1 by default)
Definition: Hit.h:229
short int Multiplicity() const
How many hits could this one be shared with.
Definition: Hit.h:227
float DeadWireCount(const TjStuff &tjs, const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:1756
struct of temporary 3D vertices
virtual double ConvertXToTicks(double X, int p, int t, int c) const =0
float PeakAmplitude() const
The estimated amplitude of the hit at its peak, in ADC units.
Definition: Hit.h:222
int TDCtick_t
Type representing a TDC tick.
Definition: RawTypes.h:24
int cmp(WireID const &other) const
Returns < 0 if this is smaller than tpcid, 0 if equal, > 0 if larger.
Definition: geo_types.h:344
Int_t max
Definition: plot.C:27
void hits()
Definition: readHits.C:15
TCanvas * c1
Definition: plotHisto.C:7
TCanvas * c2
Definition: plot_hist.C:75
virtual double Temperature() const =0
bool lessThan(CluLen c1, CluLen c2)
T get(std::string const &key) const
Definition: ParameterSet.h:231
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
bool SortByLowHit(unsigned int i, unsigned int j)
std::string PrintHit(const TCHit &hit)
Definition: Utils.cxx:4732
bool has_key(std::string const &key) const
virtual unsigned int NumberTimeSamples() const =0
The data type to uniquely identify a TPC.
Definition: geo_types.h:195
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:258
Declaration of cluster object.
float bin[41]
Definition: plottest35.C:14
raw::TDCtick_t StartTick() const
Initial tdc tick for hit.
Definition: Hit.h:217
float PeakTimeMinusRMS(float sigmas=+1.) const
Returns a time sigmas RMS away from the peak time.
Definition: Hit.h:240
bool greaterThan(CluLen c1, CluLen c2)
Detector simulation of raw signals on wires.
constexpr auto absDiff(A const &a, B const &b)
Returns the absolute value of the difference between two values.
Definition: NumericUtils.h:43
raw::TDCtick_t EndTick() const
Final tdc tick for hit.
Definition: Hit.h:218
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
virtual double ConvertTicksToX(double ticks, int p, int t, int c) const =0
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:219
std::string to_string(Flag_t< Storage > const flag)
Convert a flag into a stream (shows its index).
Definition: BitMask.h:187
Int_t min
Definition: plot.C:26
virtual double DriftVelocity(double efield=0., double temperature=0.) const =0
geo::PlaneID DecodeCTP(CTP_t CTP)
Definition: DataStructs.cxx:89
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:45
Exception thrown on invalid wire number.
Definition: Exceptions.h:42
float SummedADC() const
The sum of calibrated ADC counts of the hit (0. by default)
Definition: Hit.h:224
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
float SigmaPeakTime() const
Uncertainty for the signal peak, in tick units.
Definition: Hit.h:220
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:49
float PeakTimePlusRMS(float sigmas=+1.) const
Returns a time sigmas RMS away from the peak time.
Definition: Hit.h:237
virtual double Efield(unsigned int planegap=0) const =0
Returns the nominal electric field in the specified volume.
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:27
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
Float_t e
Definition: plot.C:34
geo::WireID suggestedWireID() const
Returns a better wire ID.
Definition: Exceptions.h:120
recob::tracking::Plane Plane
Definition: TrackState.h:17
Float_t X
Definition: plot.C:39
raw::ChannelID_t Channel() const
ID of the readout channel the hit was extracted from.
Definition: Hit.h:231
void LocalToWorld(const double *tpc, double *world) const
Transform point from local TPC frame to world frame.
Definition: TPCGeo.h:490
art framework interface to geometry description
Encapsulate the construction of a single detector plane.