LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
TrajClusterAlg.cxx
Go to the documentation of this file.
1 
20 
21 class TH1F;
22 class TH2F;
23 class TProfile;
24 
25 struct SortEntry{
26  unsigned int index;
27  float val;
28 };
29 
30 bool valDecreasing (SortEntry c1, SortEntry c2) { return (c1.val > c2.val);}
31 bool valIncreasing (SortEntry c1, SortEntry c2) { return (c1.val < c2.val);}
32 
33 namespace tca {
34 
35  bool TrajClusterAlg::SortByMultiplet(TCHit const& a, TCHit const& b)
36  {
37  // compare the wire IDs first:
38  int cmp_res = a.ArtPtr->WireID().cmp(b.ArtPtr->WireID());
39  if (cmp_res != 0) return cmp_res < 0; // order is decided, unless equal
40  // decide by start time
41  if (a.StartTick != b.StartTick) return a.StartTick < b.StartTick;
42  // if still undecided, resolve by local index
43  return a.LocalIndex < b.LocalIndex; // if still unresolved, it's a bug!
44  } // SortByMultiplet
45 
46  //------------------------------------------------------------------------------
47 
48  TrajClusterAlg::TrajClusterAlg(fhicl::ParameterSet const& pset)
49  :fCaloAlg(pset.get<fhicl::ParameterSet>("CaloAlg")), fMVAReader("Silent")
50  {
52  reconfigure(pset);
53  tjs.caloAlg = &fCaloAlg;
55  hist.CreateHists(*tfs);
56  tm.Initialize();
57  }
58 
59  //------------------------------------------------------------------------------
61  {
62 
63  bool badinput = false;
64  fHitFinderModuleLabel = pset.get<art::InputTag>("HitFinderModuleLabel");
65  fHitTruthModuleLabel = pset.get<art::InputTag>("HitTruthModuleLabel", "NA");
66  fMakeNewHits = pset.get< bool >("MakeNewHits", true);
67  fMode = pset.get< short >("Mode", 1);
68  fHitErrFac = pset.get< float >("HitErrFac", 0.4);
69  fMinAmp = pset.get< float >("MinAmp", 0.1);
70  tjs.AngleRanges = pset.get< std::vector<float>>("AngleRanges");
71  tjs.NPtsAve = pset.get< short >("NPtsAve", 20);
72  fMinPtsFit = pset.get< std::vector<unsigned short >>("MinPtsFit");
73  fMinPts = pset.get< std::vector<unsigned short >>("MinPts");
74  fMaxAngleCode = pset.get< std::vector<unsigned short>>("MaxAngleCode");
75  fMinMCSMom = pset.get< std::vector<short >>("MinMCSMom");
76 
77  fMaxChi = pset.get< float >("MaxChi", 10);
78  tjs.ChargeCuts = pset.get< std::vector<float >>("ChargeCuts", {3, 0.15, 0.25});
79  fMultHitSep = pset.get< float >("MultHitSep", 2.5);
80  tjs.KinkCuts = pset.get< std::vector<float >>("KinkCuts", {0.4, 1.5, 4});
81  fQualityCuts = pset.get< std::vector<float >>("QualityCuts", {0.8, 3});
82  fMaxWireSkipNoSignal = pset.get< float >("MaxWireSkipNoSignal", 1);
83  fMaxWireSkipWithSignal= pset.get< float >("MaxWireSkipWithSignal", 100);
84  fProjectionErrFactor = pset.get< float >("ProjectionErrFactor", 2);
85  fVLAStepSize = pset.get< float >("VLAStepSize", 1.5);
86  fJTMaxHitSep2 = pset.get< float >("JTMaxHitSep", 2);
87 
88  std::vector<std::string> skipAlgsVec = pset.get< std::vector<std::string> >("SkipAlgs");
89 
90  std::vector<std::string> specialAlgsVec;
91  if(pset.has_key("SpecialAlgs")) specialAlgsVec = pset.get< std::vector<std::string> >("SpecialAlgs");
92 
93  tjs.DeltaRayTag = pset.get< std::vector<short>>("DeltaRayTag", {-1, -1, -1});
94  tjs.MuonTag = pset.get< std::vector<short>>("MuonTag", {-1, -1, -1, - 1});
95  tjs.ShowerTag = pset.get< std::vector<float>>("ShowerTag", {-1, -1, -1, -1, -1, -1});
96  std::string fMVAShowerParentWeights = "NA";
97  pset.get_if_present<std::string>("MVAShowerParentWeights", fMVAShowerParentWeights);
98 
99  tjs.SaveShowerTree = pset.get< bool >("SaveShowerTree", false);
100  tjs.SaveCRTree = pset.get< bool >("SaveCRTree", false);
101  tjs.TagCosmics = pset.get< bool >("TagCosmics", false);
102  fChkStopCuts = pset.get< std::vector<float>>("ChkStopCuts", {-1, -1, -1});
103  fMaxTrajSep = pset.get< float >("MaxTrajSep", 4);
104 
105  fStudyMode = pset.get< bool >("StudyMode", false);
106  tjs.TestBeam = pset.get< bool >("TestBeam", false);
107  tjs.MatchTruth = pset.get< std::vector<float> >("MatchTruth", {-1, -1, -1, -1});
108  tjs.Vertex2DCuts = pset.get< std::vector<float >>("Vertex2DCuts", {-1, -1, -1, -1, -1, -1, -1});
109  tjs.Vertex3DCuts = pset.get< std::vector<float >>("Vertex3DCuts", {-1, -1});
110  tjs.VertexScoreWeights = pset.get< std::vector<float> >("VertexScoreWeights");
111  tjs.Match3DCuts = pset.get< std::vector<float >>("Match3DCuts", {-1, -1, -1, -1, -1});
112  pset.get_if_present<std::vector<float>>("NeutralVxCuts", tjs.NeutralVxCuts);
113 
114  debug.Cryostat = 0;
115  debug.TPC = 0;
116  if(pset.has_key("DebugCryostat")) debug.Cryostat = pset.get< int >("DebugCryostat");
117  if(pset.has_key("DebugTPC")) debug.TPC = pset.get< int >("DebugTPC");
118  debug.Plane = pset.get< int >("DebugPlane", -1);
119  debug.Wire = pset.get< int >("DebugWire", -1);
120  debug.Tick = pset.get< int >("DebugTick", -1);
121  debug.WorkID = pset.get< int >("DebugWorkID", 0);
122 
123  // convert the max traj separation into a separation^2
124  fMaxTrajSep *= fMaxTrajSep;
126 
127  // in the following section we ensure that the fcl vectors are appropriately sized so that later references are valid
128  if(fMinPtsFit.size() != fMinPts.size()) badinput = true;
129  if(fMaxAngleCode.size() != fMinPts.size()) badinput = true;
130  if(fMinMCSMom.size() != fMinPts.size()) badinput = true;
131  if(badinput) throw art::Exception(art::errors::Configuration)<< "Bad input from fcl file. Vector lengths for MinPtsFit, MaxAngleRange and MinMCSMom should be defined for each reconstruction pass";
132 
133  if(tjs.Vertex2DCuts.size() < 10) throw art::Exception(art::errors::Configuration)<<"Vertex2DCuts must be size 10\n 0 = Max length definition for short TJs\n 1 = Max vtx-TJ sep short TJs\n 2 = Max vtx-TJ sep long TJs\n 3 = Max position pull for >2 TJs\n 4 = Max vtx position error\n 5 = Min MCSMom for one of two TJs\n 6 = Min fraction of wires hit btw vtx and Tjs\n 7 = Min Score\n 8 = min ChgFrac at a vtx or merge point\n 9 = max MCSMom asymmetry";
134  if(tjs.Vertex3DCuts.size() < 2) throw art::Exception(art::errors::Configuration)<<"Vertex3DCuts must be size 2\n 0 = Max dX (cm)\n 1 = Max pull";
135  if(tjs.KinkCuts.size() != 3) throw art::Exception(art::errors::Configuration)<<"KinkCuts must be size 2\n 0 = Hard kink angle cut\n 1 = Kink angle significance\n 2 = nPts fit";
136  if(tjs.KinkCuts[2] < 2) throw art::Exception(art::errors::Configuration)<<"KinkCuts[2] must be > 1";
137  if(tjs.ChargeCuts.size() != 3) throw art::Exception(art::errors::Configuration)<<"ChargeCuts must be size 3\n 0 = Charge pull cut\n 1 = Min allowed fractional chg RMS\n 2 = Max allowed fractional chg RMS";
138 
139  if(tjs.MuonTag.size() != 4) throw art::Exception(art::errors::Configuration)<<"MuonTag must be size 4\n 0 = minPtsFit\n 1 = minMCSMom\n 2= maxWireSkipNoSignal\n 3 = min delta ray length for tagging";
140  if(tjs.DeltaRayTag.size() != 3) throw art::Exception(art::errors::Configuration)<<"DeltaRayTag must be size 3\n 0 = Max endpoint sep\n 1 = min MCSMom\n 2 = max MCSMom";
141  if(fChkStopCuts.size() != 3) throw art::Exception(art::errors::Configuration)<<"ChkStopCuts must be size 3\n 0 = Min Charge ratio\n 1 = Charge slope pull cut\n 2 = Charge fit chisq cut";
142  if(tjs.ShowerTag.size() < 13) {
143  std::cout<< "ShowerTag must be size 13\n 0 = Mode\n 1 = max MCSMom\n 2 = max separation (WSE units)\n 3 = Max angle diff\n 4 = Factor * rms width\n 5 = Min half width\n 6 = min total Tps\n 7 = Min Tjs\n 8 = max parent FOM\n 9 = max direction FOM 10 = max AspectRatio\n 11 = min Score to preserve a vertex\n 12 = Debug showers in CTP\n";
144  std::cout<<" Fixing this problem...";
145  tjs.ShowerTag.resize(13);
146  // set the min score to 0
147  tjs.ShowerTag[11] = 0;
148  // turn off printing
149  tjs.ShowerTag[12] = -1;
150  }
151  if(tjs.Match3DCuts.size() < 7) {
152  std::cout<<">>>>>>>> Match3DCuts has been expanded to size 7. Please update your fcl file\n";
153  unsigned short oldsize = tjs.Match3DCuts.size();
154  tjs.Match3DCuts.resize(7);
155  if(oldsize < 5) std::cout<<" Setting Match3DCuts[4] = 2000 combinations\n";
156  if(oldsize < 6) std::cout<<" Setting Match3DCuts[5] = 1, which disables charge asymmetry checking\n";
157  tjs.Match3DCuts[4] = 2000;
158  tjs.Match3DCuts[5] = 1;
159  tjs.Match3DCuts[6] = tjs.KinkCuts[0];
160  }
161  // convert Match3DCuts[6] from angle to cos(angle)
162  tjs.Match3DCuts[6] = cos(tjs.Match3DCuts[6]);
163 
164  // check the angle ranges and convert from degrees to radians
165  if(tjs.AngleRanges.back() < 90) {
166  mf::LogVerbatim("TC")<<"Last element of AngleRange != 90 degrees. Fixing it\n";
167  tjs.AngleRanges.back() = 90;
168  }
169 
170  // decide whether debug information should be printed
171  bool debugTj = debug.Cryostat >= 0 && debug.TPC >= 0 && debug.Plane >= 0 && debug.Wire >= 0 && debug.Tick >= 0;
172  if(debugTj) debug.CTP = EncodeCTP((unsigned int)debug.Cryostat, (unsigned int)debug.TPC, (unsigned int)debug.Plane);
173  bool debugMerge = (debug.Cryostat >= 0 && debug.TPC >= 0 && debug.Wire < 0);
174  bool debugVtx = (debug.Cryostat >= 0 && debug.TPC >= 0 && debug.Tick < 0);
175  bool debugWorkID = (debug.Cryostat >= 0 && debug.TPC >= 0 && debug.WorkID < 0);
176  tjs.DebugMode = (debugTj || debugMerge || debugVtx || debugWorkID);
177  if(tjs.DebugMode) {
178  std::cout<<"**************** Debug mode: debug.CTP "<<debug.CTP<<" ****************\n";
179  std::cout<<"Cryostat "<<debug.Cryostat<<" TPC "<<debug.TPC<<" Plane "<<debug.Plane<<"\n";
180  std::cout<<"Pass MinPts MinPtsFit Max Angle\n";
181  for(unsigned short pass = 0; pass < fMinPts.size(); ++pass) {
182  unsigned short ir = fMaxAngleCode[pass];
183  if(ir > tjs.AngleRanges.size() - 1) ir = tjs.AngleRanges.size() - 1;
184  std::cout<<std::setw(3)<<pass;
185  std::cout<<std::setw(7)<<fMinPts[pass];
186  std::cout<<std::setw(7)<<fMinPtsFit[pass];
187  std::cout<<std::setw(12)<<(int)tjs.AngleRanges[ir]<<"\n";
188  }
189  std::cout<<"Max angle ranges\n range degrees radians\n";
190  for(unsigned short ir = 0; ir < tjs.AngleRanges.size(); ++ir) {
191  std::cout<<std::setw(3)<<ir;
192  std::cout<<std::setw(8)<<(int)tjs.AngleRanges[ir];
193  std::cout<<std::setw(8)<<std::setprecision(3)<<tjs.AngleRanges[ir] * M_PI / 180;
194  std::cout<<"\n";
195  } // ir
196  }
197 
198  for(auto& range : tjs.AngleRanges) {
199  if(range < 0 || range > 90) throw art::Exception(art::errors::Configuration)<< "Invalid angle range "<<range<<" Must be 0 - 90 degrees";
200  range *= M_PI / 180;
201  } // range
202 
203  // Ensure that the size of the AlgBitNames vector is consistent with the AlgBit typedef enum
204  if(kAlgBitSize != AlgBitNames.size()) throw art::Exception(art::errors::Configuration)<<"kAlgBitSize "<<kAlgBitSize<<" != AlgBitNames size "<<AlgBitNames.size();
205  fAlgModCount.resize(kAlgBitSize);
206 
207  if(kFlagBitSize != StopFlagNames.size()) throw art::Exception(art::errors::Configuration)<<"kFlagBitSize "<<kFlagBitSize<<" != StopFlagNames size "<<StopFlagNames.size();
208 
209  if(kFlagBitSize > 64) throw art::Exception(art::errors::Configuration)<<"Increase the size of UseAlgs to at least "<<kFlagBitSize;
210 
211  bool gotit, printHelp = false;
212  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) tjs.UseAlg[ib] = true;
213 
214  // turn off the special algs
215  // A lightly tested algorithm that should only be turned on for testing
216  tjs.UseAlg[kStopAtTj] = false;
217  // Do an exhaustive (and slow) check of the hit -> trajectory associations
218  tjs.UseAlg[kChkInTraj] = false;
219 
220  for(auto strng : skipAlgsVec) {
221  gotit = false;
222  if(strng == "All") {
223  // turn everything off
224  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) tjs.UseAlg[ib] = false;
225  gotit = true;
226  break;
227  } // All off
228  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) {
229  if(strng == AlgBitNames[ib]) {
230  tjs.UseAlg[ib] = false;
231  gotit = true;
232  break;
233  }
234  } // ib
235  if(!gotit) {
236  std::cout<<"******* Unknown SkipAlgs input string '"<<strng<<"'\n";
237  printHelp = true;
238  }
239  } // strng
240  if(printHelp) {
241  std::cout<<"Valid AlgNames:";
242  for(auto strng : AlgBitNames) std::cout<<" "<<strng;
243  std::cout<<"\n";
244  std::cout<<"Or specify All to turn all algs off\n";
245  throw art::Exception(art::errors::Configuration)<< "Invalid SkipAlgs specification";
246  }
247  // overwrite any settings above with special algs
248  for(auto strng : specialAlgsVec) {
249  gotit = false;
250  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) {
251  if(strng == AlgBitNames[ib]) {
252  tjs.UseAlg[ib] = true;
253  gotit = true;
254  break;
255  }
256  } // ib
257  if(!gotit) {
258  std::cout<<"******* Unknown SpecialAlgs input string '"<<strng<<"'\n";
259  printHelp = true;
260  }
261  } // strng
262  if(printHelp) {
263  std::cout<<"Valid AlgNames:";
264  for(auto strng : AlgBitNames) std::cout<<" "<<strng;
265  std::cout<<"\n";
266  std::cout<<"Or specify All to turn all algs off\n";
267  throw art::Exception(art::errors::Configuration)<< "Invalid SkipAlgs specification";
268  }
269 
270  // Configure the TMVA reader for the shower parent BDT
271  if(fMVAShowerParentWeights != "NA" && tjs.ShowerTag[0] > 0) ConfigureMVA(tjs, fMVAShowerParentWeights);
272 
273  if(tjs.DebugMode) {
274  std::cout<<"Using algs:";
275  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) {
276  if(ib % 10 == 0) std::cout<<"\n";
277  if(tjs.UseAlg[ib] && ib != kKilled) std::cout<<" "<<AlgBitNames[ib];
278  }
279  std::cout<<"\n";
280  std::cout<<"Skipping algs:";
281  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(!tjs.UseAlg[ib] && ib != kKilled) std::cout<<" "<<AlgBitNames[ib];
282  std::cout<<"\n";
283  }
284  tjs.EventsProcessed = 0;
285  fUseOldBackTracker = false;
286 
287  } // reconfigure
288 
291  tjs.vtx3 = {};
292  tjs.vtx = {};
293  tjs.tcl = {};
294  tjs.matchVec = {};
295  tjs.pfps = {};
296  tjs.WireHitRange = {};
297  tjs.fHits = {};
298  tjs.allTraj = {};
299  tjs.mallTraj = {};
300  tjs.cots = {};
301  tjs.dontCluster = {};
302  tjs.showers = {};
303  tjs.MCPartList = {};
304 
306  ClearCRInfo(tjs);
307 
308  } // ClearResults()
309 
312  {
313 
314  fIsRealData = evt.isRealData();
315 
316  // a gratuitous clearing of everything before we start
317  ClearResults();
319 
320  tjs.detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
321  tjs.geom = lar::providerFrom<geo::Geometry>();
322 
323  // Get the hits and associations
324  GetHitCollection(evt);
325  if(tjs.fHits.empty()) return;
326 
327  if(fMode == 0) return;
328 
329  // check for debugging mode triggered by Plane, Wire, Tick
330  debug.Hit = UINT_MAX;
331  if(tjs.DebugMode) {
332  std::cout<<"Look for hit near Cryo:TPC:Pln:Wire:Tick "<<debug.Cryostat;
333  std::cout<<":"<<debug.TPC<<":"<<debug.Plane<<":"<<debug.Wire<<":"<<debug.Tick;
334  for(unsigned int iht = 0; iht < tjs.fHits.size(); ++iht) {
335  auto& hit = tjs.fHits[iht];
336  if((int)hit.ArtPtr->WireID().Plane != debug.Plane) continue;
337  if((int)hit.ArtPtr->WireID().Wire != debug.Wire) continue;
338  if(tjs.fHits[iht].PeakTime < debug.Tick - 5) continue;
339  if(tjs.fHits[iht].PeakTime > debug.Tick + 5) continue;
340  debug.Hit = iht;
341  std::cout<<" Found iht "<<iht<<" "<<debug.Plane<<":"<<PrintHit(tjs.fHits[iht]);
342  std::cout<<" Amp "<<(int)tjs.fHits[iht].PeakAmplitude;
343  std::cout<<" RMS "<<std::fixed<<std::setprecision(1)<<tjs.fHits[iht].RMS;
344  std::cout<<" Chisq "<<std::fixed<<std::setprecision(1)<<tjs.fHits[iht].GoodnessOfFit;
345  std::cout<<" Mult "<<tjs.fHits[iht].Multiplicity;
346  std::cout<<"\n";
347  break;
348  } // iht
349  if(debug.Hit == UINT_MAX) std::cout<<" not found\n";
350  } // debugging mode
351 
352  tjs.Run = evt.run();
353  tjs.SubRun = evt.subRun();
354  tjs.Event = evt.event();
355  tjs.SelectEvent = false;
356  fWorkID = 0;
357 
358  // Set true if a truly bad situation occurs
359  fQuitAlg = false;
360  mrgPrt = false;
361  didPrt = false;
362 
363  if(fMode > 0) {
364  // Step from upstream (small wire number) to downstream (large wire number)
365  tjs.StepDir = 1;
366  } else {
367  // or step in the other direction
368  tjs.StepDir = -1;
369  }
370  if(fMode == 4) std::cout<<"RTCA: fMode set to 4 for debugging\n";
371  for (const geo::TPCID& tpcid: tjs.geom->IterateTPCIDs()) {
372  geo::TPCGeo const& TPC = tjs.geom->TPC(tpcid);
373  // special debug mode for multi-TPC detectors like protoDUNE
374  if(fMode == 4 && (int)tpcid.TPC != debug.TPC) continue;
375  fQuitAlg = !FillWireHitRange(tjs, tpcid);
376  if(fQuitAlg) return;
377  for(unsigned short plane = 0; plane < TPC.Nplanes(); ++plane) {
378  // special mode for only reconstructing the collection plane
379  if(fMode == 2 && plane != TPC.Nplanes() - 1) continue;
380  // special debug mode for multi-TPC detectors like protoDUNE
381  if(fMode == 4 && (int)plane != debug.Plane) continue;
382  // no hits on this plane?
383  if(tjs.FirstWire[plane] > tjs.LastWire[plane]) continue;
384  // Set the CTP code to ensure objects are compared within the same plane
385  CTP_t inCTP = EncodeCTP(tpcid.Cryostat, tpcid.TPC, plane);
386  // reconstruct all trajectories in the current plane
387  ReconstructAllTraj(inCTP);
388  if(fQuitAlg) {
389  mf::LogVerbatim("TC")<<"Found fQuitAlg after ReconstructAllTraj";
390  ClearResults();
391  return;
392  }
393  } // plane
394 
395  // No sense taking muon direction if delta ray tagging is disabled
397  Find3DVertices(tjs, tpcid);
398  // Look for incomplete 3D vertices that won't be recovered because there are
399  // missing trajectories in a plane
400  FindMissedVxTjs(tpcid);
401  prt = (debug.Plane >= tjs.NumPlanes && debug.Tick == 6666);
402  ScoreVertices(tjs, tpcid, prt);
403  // This is done in ChkStop
404 // TagProtons(tjs, tpcid, prt);
405  // Define the ParentID of trajectories using the vertex score
406  DefineTjParents(tjs, tpcid, prt);
407  for(unsigned short plane = 0; plane < TPC.Nplanes(); ++plane) {
408  CTP_t inCTP = EncodeCTP(tpcid.Cryostat, tpcid.TPC, plane);
409  if(!ChkVtxAssociations(tjs, inCTP)) {
410  std::cout<<"RTC: ChkVtxAssociations found an error\n";
411  }
412  } // plane
413  if(tjs.Match3DCuts[0] > 0) {
414  // Fill tjs.mallTraj and tjs.MatchVec
415  bool prt = (debug.Plane >= 0) && (debug.Tick == 3333);
416  FillmAllTraj(tjs, tpcid);
417  FindPFParticles(tjs, tpcid, prt);
418  DefinePFPParents(tjs, tpcid, prt);
419  if(tjs.TagCosmics) {
420  for(auto& pfp : tjs.pfps) {
421  if(pfp.ID == 0) continue;
422  if(pfp.TPCID != tpcid) continue;
423  SaveCRInfo(tjs, pfp, prt, fIsRealData);
424  } // pfp
425  } // TagCosmics
426  } // 3D matching requested
427  KillPoorVertices(tjs, tpcid);
428  FindNeutralVertices(tjs, tpcid);
429  // Use 3D matching information to find showers in 2D. FindShowers3D returns
430  // true if the algorithm was successful indicating that the matching needs to be redone
431  if(tjs.ShowerTag[0] == 2 || tjs.ShowerTag[0] == 4) {
432  FindShowers3D(tjs, tpcid);
433  if(tjs.SaveShowerTree) {
434  std::cout << "SHOWER TREE STAGE NUM SIZE: " << tjs.stv.StageNum.size() << std::endl;
435  showertree->Fill();
436  }
437  } // 3D shower code
438  } // tpcid
439 
441  // Convert trajectories in allTraj into clusters
443  // Ensure that all PFParticles have a start vertex
445  if(fQuitAlg) {
446  mf::LogVerbatim("TC")<<"RunTrajCluster failed in MakeAllTrajClusters";
447  ClearResults();
448  return;
449  }
450 /*
451  if(!CheckHitClusterAssociations(tjs)) {
452  ClearResults();
453  mf::LogVerbatim("TC")<<"RunTrajCluster failed in CheckHitClusterAssociations";
454  return;
455  }
456 */
457 
459  if (tjs.SaveCRTree) crtree->Fill();
460 
461  // fill some basic histograms
462  if(fStudyMode) {
463  for(auto& vx2 : tjs.vtx) if(vx2.ID > 0 && vx2.Score > 0) hist.fVx2Score->Fill(vx2.Score);
464  for(auto& vx3 : tjs.vtx3) if(vx3.ID > 0 && vx3.Score > 0) hist.fVx3Score->Fill(vx3.Score);
465  }
466 
467  // print trajectory summary report?
468  if(tjs.ShowerTag[0] > 1 && tjs.ShowerTag[12] < tjs.NumPlanes) debug.Plane = tjs.ShowerTag[12];
469  if(tjs.DebugMode) {
470  mf::LogVerbatim("TC")<<"Done in RunTrajClusterAlg";
471  if(tjs.ShowerTag[1] > 1) {
472  Print2DShowers("RTC", tjs, USHRT_MAX, false);
473  PrintShowers("RTC", tjs);
474  }
475  if(tjs.Match3DCuts[0] > 0) PrintPFPs("RTC", tjs);
476  PrintAllTraj("RTC", tjs, debug, USHRT_MAX, 0);
477  }
478 
479  unsigned short ntj = 0;
480  unsigned short nsh = 0;
481  for(auto& tj : tjs.allTraj) {
482  if(tj.AlgMod[kKilled]) continue;
483  ++ntj;
484  if(tj.AlgMod[kShowerTj]) ++nsh;
485  } // tj
486  if(tjs.DebugMode) std::cout<<"RTC done ntjs "<<ntj<<" nshowers "<<nsh<<" events processed "<<tjs.EventsProcessed<<"\n";
487 
488  if(tjs.MatchTruth[0] >= 0) tm.PrintResults(tjs.Event);
489 
490  // convert vertex time from WSE to ticks
491  for(auto& avtx : tjs.vtx) avtx.Pos[1] /= tjs.UnitsPerTick;
492 
493  if(tjs.DebugMode) mf::LogVerbatim("TC")<<"RunTrajCluster success run "<<tjs.Run<<" event "<<tjs.Event<<" allTraj size "<<tjs.allTraj.size()<<" events processed "<<tjs.EventsProcessed;
494 
495  } // RunTrajClusterAlg
496 
499  {
500  // Reconstruct clusters in inCTP and put them in allTraj
501 
502  unsigned short plane = DecodeCTP(inCTP).Plane;
503  unsigned int nwires = tjs.LastWire[plane] - tjs.FirstWire[plane] - 1;
504 
505  // turn of trajectory printing
506  TJPrt = 0;
507  didPrt = false;
508 
509  // Make several passes through the hits with user-specified cuts for each
510  // pass. In general these are to not reconstruct large angle trajectories on
511  // the first pass
512  float maxHitsRMS = 4 * tjs.AveHitRMS[plane];
513  for(unsigned short pass = 0; pass < fMinPtsFit.size(); ++pass) {
514  for(unsigned int ii = 0; ii < nwires; ++ii) {
515  // decide which way to step given the sign of StepDir
516  unsigned int iwire = 0;
517  unsigned int jwire = 0;
518  if(tjs.StepDir > 0) {
519  // step DS
520  iwire = tjs.FirstWire[plane] + ii;
521  jwire = iwire + 1;
522  } else {
523  // step US
524  iwire = tjs.LastWire[plane] - ii - 1;
525  jwire = iwire - 1;
526  }
527  if(iwire > tjs.WireHitRange[plane].size() - 1) continue;
528  if(jwire > tjs.WireHitRange[plane].size() - 1) continue;
529  // skip bad wires or no hits on the wire
530  if(tjs.WireHitRange[plane][iwire].first < 0) continue;
531  if(tjs.WireHitRange[plane][jwire].first < 0) continue;
532  unsigned int ifirsthit = (unsigned int)tjs.WireHitRange[plane][iwire].first;
533  unsigned int ilasthit = (unsigned int)tjs.WireHitRange[plane][iwire].second;
534  unsigned int jfirsthit = (unsigned int)tjs.WireHitRange[plane][jwire].first;
535  unsigned int jlasthit = (unsigned int)tjs.WireHitRange[plane][jwire].second;
536  for(unsigned int iht = ifirsthit; iht < ilasthit; ++iht) {
537  prt = (iht == debug.Hit);
538  if(prt) {
539  mf::LogVerbatim("TC")<<"+++++++ Pass "<<pass<<" Found debug hit "<<plane<<":"<<PrintHit(tjs.fHits[iht]);
540  didPrt = true;
541  }
542  // Only consider hits that are available
543  if(tjs.fHits[iht].InTraj != 0) continue;
544  // We hope to make a trajectory point at the hit position of iht in WSE units
545  // with a direction pointing to jht
546  unsigned int fromWire = tjs.fHits[iht].ArtPtr->WireID().Wire;
547  float fromTick = tjs.fHits[iht].PeakTime;
548  float iqtot = tjs.fHits[iht].Integral;
549  float hitsRMSTick = tjs.fHits[iht].RMS;
550  std::vector<unsigned int> iHitsInMultiplet;
551  if(pass == 0) {
552  // only use the hit on the first pass
553  iHitsInMultiplet.resize(1);
554  iHitsInMultiplet[0] = iht;
555  } else {
556  GetHitMultiplet(iht, iHitsInMultiplet);
557  // ignore very high multiplicities
558  if(iHitsInMultiplet.size() > 4) continue;
559  }
560  if(iHitsInMultiplet.size() > 1) {
561  fromTick = HitsPosTick(tjs, iHitsInMultiplet, iqtot, kUnusedHits);
562  hitsRMSTick = HitsRMSTick(tjs, iHitsInMultiplet, kUnusedHits);
563  }
564  if(hitsRMSTick == 0) continue;
565  bool fatIHit = (hitsRMSTick > maxHitsRMS);
566  if(prt) mf::LogVerbatim("TC")<<" hit RMS "<<tjs.fHits[iht].RMS<<" BB Multiplicity "<<iHitsInMultiplet.size()<<" AveHitRMS["<<plane<<"] "<<tjs.AveHitRMS[plane]<<" HitsRMSTick "<<hitsRMSTick<<" fatIHit "<<fatIHit;
567  for(unsigned int jht = jfirsthit; jht < jlasthit; ++jht) {
568  // Only consider hits that are available
569  if(tjs.fHits[iht].InTraj != 0) continue;
570  if(tjs.fHits[jht].InTraj != 0) continue;
571  prt = (iht == debug.Hit);
572  // clear out any leftover work inTraj's that weren't cleaned up properly
573  for(auto& hit : tjs.fHits) {
574  if(hit.InTraj < 0) {
575  std::cout<<"Dirty hit "<<PrintHit(hit)<<" EventsProcessed "<<tjs.EventsProcessed<<" fWorkID "<<fWorkID<<"\n";
576  hit.InTraj = 0;
577  }
578  }
579  unsigned int toWire = jwire;
580  float toTick = tjs.fHits[jht].PeakTime;
581  float jqtot = tjs.fHits[jht].Integral;
582  std::vector<unsigned int> jHitsInMultiplet;
583  if(pass == 0) {
584  // only use the hit on the first pass
585  jHitsInMultiplet.resize(1);
586  jHitsInMultiplet[0] = jht;
587  } else {
588  GetHitMultiplet(jht, jHitsInMultiplet);
589  // ignore very high multiplicities
590  if(jHitsInMultiplet.size() > 4) continue;
591  }
592  hitsRMSTick = HitsRMSTick(tjs, jHitsInMultiplet, kUnusedHits);
593  if(hitsRMSTick == 0) continue;
594  bool fatJHit = (hitsRMSTick > maxHitsRMS);
595  if(pass == 0) {
596  // require both hits to be consistent
597  if((fatIHit && !fatJHit) || (!fatIHit && fatJHit)) {
598  if(prt) mf::LogVerbatim("TC")<<" jht fails "<<PrintHit(tjs.fHits[jht])<<" hit RMS "<<tjs.fHits[jht].RMS<<" mRMS "<<hitsRMSTick<<" fatJhit "<<fatJHit<<" max RMS "<<maxHitsRMS;
599  continue;
600  }
601  } else {
602  // pass > 0
603  if(jHitsInMultiplet.size() > 1) toTick = HitsPosTick(tjs, jHitsInMultiplet, jqtot, kUnusedHits);
604  }
605  bool hitsOK = TrajHitsOK(tjs, iHitsInMultiplet, jHitsInMultiplet);
606  if(prt) {
607  mf::LogVerbatim("TC")<<"+++++++ checking TrajHitsOK with jht "<<plane<<":"<<PrintHit(tjs.fHits[jht])<<" BB Multiplicity "<<jHitsInMultiplet.size()<<" HitsRMSTick "<<HitsRMSTick(tjs, jHitsInMultiplet, kUnusedHits)<<" fatJhit "<<fatJHit<<" setting toTick to "<<(int)toTick<<" TrajHitsOK "<<hitsOK;
608  }
609  // Ensure that the hits StartTick and EndTick have the proper overlap
610  if(!hitsOK) continue;
611  // start a trajectory with direction from iht -> jht
612  Trajectory work;
613  if(!StartTraj(work, fromWire, fromTick, toWire, toTick, inCTP, pass)) continue;
614  if(didPrt) TJPrt = work.WorkID;
615  // check for a major failure
616  if(fQuitAlg) return;
617  if(work.Pts.empty()) {
618  if(prt) mf::LogVerbatim("TC")<<"ReconstructAllTraj: StartTraj failed";
619  continue;
620  }
621  // check for a large angle crawl
622  if(work.Pts[0].AngleCode > fMaxAngleCode[work.Pass]) {
623  if(prt) mf::LogVerbatim("TC")<<"ReconstructAllTraj: Wrong angle code "<<work.Pts[0].AngleCode<<" for this pass "<<work.Pass;
624  ReleaseHits(tjs, work);
625  continue;
626  }
627  work.Pts[0].DeltaRMS = fHitErrFac * tjs.UnitsPerTick * hitsRMSTick;
628  // don't include the charge of iht since it will be low if this
629  // is a starting/ending track
630  work.AveChg = jqtot;
631  // try to add close hits
632  bool sigOK;
633  AddHits(work, 0, sigOK);
634  // check for a major failure
635  if(fQuitAlg) return;
636  if(!sigOK || work.Pts[0].Chg == 0) {
637  if(prt) mf::LogVerbatim("TC")<<" No hits at initial trajectory point ";
638  ReleaseHits(tjs, work);
639  continue;
640  }
641  // move the TP position to the hit position but don't mess with the direction
642  work.Pts[0].Pos = work.Pts[0].HitPos;
643  // print the header and the first TP
644  if(prt) PrintTrajectory("RAT", tjs, work, USHRT_MAX);
645  // We can't update the trajectory yet because there is only one TP.
646  work.EndPt[0] = 0;
647  // now try stepping away
648  StepCrawl(work);
649  // check for a major failure
650  if(fQuitAlg) return;
651  if(prt) mf::LogVerbatim("TC")<<" After first StepCrawl. fGoodTraj "<<fGoodTraj<<" fTryWithNextPass "<<fTryWithNextPass;
652  if(!fGoodTraj && fTryWithNextPass) {
653  StepCrawl(work);
654  if(!fGoodTraj || work.NeedsUpdate) {
655  if(prt) mf::LogVerbatim("TC")<<" xxxxxxx StepCrawl failed AGAIN. fTryWithNextPass "<<fTryWithNextPass;
656  ReleaseHits(tjs, work);
657  continue;
658  } // Failed again
659  }
660  // Check the quality of the work trajectory
661  CheckTraj(work);
662  // check for a major failure
663  if(fQuitAlg) return;
664  if(prt) mf::LogVerbatim("TC")<<"ReconstructAllTraj: After CheckTraj EndPt "<<work.EndPt[0]<<"-"<<work.EndPt[1]<<" fGoodTraj "<<fGoodTraj<<" fTryWithNextPass "<<fTryWithNextPass;
665  if(fTryWithNextPass) {
666  // Most likely, the first part of the trajectory was good but the latter part
667  // had too many unused hits. The work vector was
668  // truncated and pass incremented, so give it another try
669  work.AlgMod[kTryWithNextPass] = true;
670  StepCrawl(work);
671  // check for a major failure
672  if(fQuitAlg) return;
673  if(!fGoodTraj) {
674  if(prt) mf::LogVerbatim("TC")<<" xxxxxxx StepCrawl failed AGAIN after CheckTraj";
675  ReleaseHits(tjs, work);
676  continue;
677  } // Failed again
678  } // fTryWithNextPass
679  if(prt) mf::LogVerbatim("TC")<<"StepCrawl done: fGoodTraj "<<fGoodTraj<<" NumPtsWithCharge "<<NumPtsWithCharge(tjs, work, true)<<" cut "<<fMinPts[work.Pass];
680  // decide if the trajectory is long enough for this pass
681  if(!fGoodTraj || NumPtsWithCharge(tjs, work, true) < fMinPts[work.Pass]) {
682  if(prt) mf::LogVerbatim("TC")<<" xxxxxxx Not enough points "<<NumPtsWithCharge(tjs, work, true)<<" minimum "<<fMinPts[work.Pass]<<" or !fGoodTraj";
683  ReleaseHits(tjs, work);
684  continue;
685  }
686  if(prt) mf::LogVerbatim("TC")<<"ReconstructAllTraj: calling StoreTraj with npts "<<work.EndPt[1];
687  fQuitAlg = !StoreTraj(tjs, work);
688  // check for a major failure
689  if(fQuitAlg) return;
690  if(tjs.UseAlg[kChkInTraj]) {
691  fQuitAlg = !InTrajOK(tjs, "RAT");
692  if(fQuitAlg) {
693  mf::LogVerbatim("TC")<<"InTrajOK failed in ReconstructAllTraj";
694  return;
695  }
696  } // use ChkInTraj
697  // See if it should be split
698  CheckTrajBeginChg(tjs, tjs.allTraj.size() - 1, prt);
699  break;
700  } // jht
701  } // iht
702  } // iwire
703 
704  // Ensure that all tjs are in the same order and do some clean up
705  for(auto& tj : tjs.allTraj) {
706  if(tj.AlgMod[kKilled]) continue;
707  if(tj.CTP != inCTP) continue;
708  if(tj.StepDir != tjs.StepDir && !tj.AlgMod[kSetDir]) ReverseTraj(tjs, tj);
709  // Feb 14
711  } // tj
712 
713  // Tag delta rays before merging and making vertices
714  TagDeltaRays(tjs, inCTP);
715  // Try to merge trajectories before making vertices
716  bool lastPass = (pass == fMinPtsFit.size() - 1);
717  EndMerge(inCTP, lastPass);
718  if(fQuitAlg) return;
719 
720  // TY: Split high charge hits near the trajectory end
721  ChkHiChgHits(inCTP);
722 
723  Find2DVertices(tjs, inCTP);
724  FindVtxTjs(inCTP);
725  if(fQuitAlg) return;
726 
727  } // pass
728 
729  // Merge Tjs that share a lot of hits with each other
730 // MergeGhostTjs(tjs, inCTP);
731 
732  // Use unused hits in all trajectories
733  UseUnusedHits();
734 
735  // make junk trajectories using nearby un-assigned hits
736  if(fJTMaxHitSep2 > 0) {
737  FindJunkTraj(inCTP);
738  if(fQuitAlg) return;
739  }
740  TagDeltaRays(tjs, inCTP);
741 
742  // Tag ShowerLike Tjs
743  if(tjs.ShowerTag[0] > 0) TagShowerLike("RAT", tjs, inCTP);
744 
745  Find2DVertices(tjs, inCTP);
747  // Make vertices between long Tjs and junk Tjs.
748  MakeJunkVertices(tjs, inCTP);
749  // check for a major failure
750  if(fQuitAlg) return;
751 
752  // last attempt to attach Tjs to vertices
753  bool vprt = (debug.Plane == (int)DecodeCTP(inCTP).Plane && debug.Tick == -10101);
754  for(unsigned short ivx = 0; ivx < tjs.vtx.size(); ++ivx) {
755  auto& vx2 = tjs.vtx[ivx];
756  if(vx2.ID == 0) continue;
757  if(vx2.CTP != inCTP) continue;
758  AttachAnyTrajToVertex(tjs, ivx, vprt);
759  UpdateVxEnvironment("RAT", tjs, vx2, vprt);
760  } // ivx
761 
762  // Check the Tj <-> vtx associations and define the vertex quality
763  if(!ChkVtxAssociations(tjs, inCTP)) {
764  std::cout<<"RAT: ChkVtxAssociations found an error\n";
765  }
766 
767  // TY: Improve hit assignments near vertex
768  VtxHitsSwap(tjs, inCTP);
769 
770  // Refine vertices, trajectories and nearby hits
771 // Refine2DVertices();
772 
773  } // ReconstructAllTraj
774 
777  {
778  if(tjs.allTraj.size() == 0) return;
779  if(!tjs.UseAlg[kUUH]) return;
780 
781  // max change in position allowed after adding all unused hits in a multiplet
782  float maxPosSep2 = 0.25;
783  // Relax this cut for special tracking mode
784  if(fMode == 2) maxPosSep2 = 1;
785 
786  std::vector<unsigned int> hitsInMultiplet;
787  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
788  Trajectory& tj = tjs.allTraj[itj];
789  if(tj.AlgMod[kKilled]) continue;
790  // Find the max delta
791  unsigned short firstPt = tj.EndPt[0];
792  unsigned short lastPt = tj.EndPt[1];
793  for(unsigned short ipt = firstPt; ipt <= lastPt; ++ipt) {
794  TrajPoint& tp = tj.Pts[ipt];
795  if(AngleRange(tjs, tp) == 0) continue;
796  if(tp.Hits.empty()) continue;
797  bool hitsAdded = false;
798  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
799  if(!tp.UseHit[ii]) continue;
800  unsigned int iht = tp.Hits[ii];
801  GetHitMultiplet(iht, hitsInMultiplet);
802  if(hitsInMultiplet.size() > 1) {
803  for(unsigned short jj = ii + 1; jj < tp.Hits.size(); ++jj) {
804  if(!tp.UseHit[jj]) continue;
805  if(std::find(hitsInMultiplet.begin(), hitsInMultiplet.end(), tp.Hits[jj]) != hitsInMultiplet.end()) {
806  tp.UseHit[jj] = true;
807  tjs.fHits[tp.Hits[jj]].InTraj = tj.ID;
808  hitsAdded = true;
809  }
810  } // jj
811  }
812  } // ii
813  if(hitsAdded) {
814  // save the hit position
815  std::array<float, 2> oldHitPos = tp.HitPos;
816  DefineHitPos(tp);
817  // keep it if
818  if(PosSep2(tj.Pts[ipt].HitPos, oldHitPos) < maxPosSep2) {
819  tj.AlgMod[kUUH] = true;
820  } else {
821  UnsetUsedHits(tjs, tj.Pts[ipt]);
822  }
823  }
824  } // ipt
825  if(tj.AlgMod[kUUH]) SetEndPoints(tjs, tj);
826  } // itj
827 
828  } // UseUnusedHits
829 
832  {
833  // Reverse the trajectory and step in the opposite direction. The
834  // updated trajectory is returned if this process is successful
835 
836  if(!tjs.UseAlg[kRvPrp]) return;
837 
838  if(tj.Pts.size() < 6) return;
839  // only do this once
840  if(tj.AlgMod[kRvPrp]) return;
841 
842  // This code can't handle VLA trajectories
843  if(tj.Pts[tj.EndPt[0]].AngleCode == 2) return;
844 
845  if(tj.EndPt[0] > 0) {
846  tj.Pts.erase(tj.Pts.begin(), tj.Pts.begin() + tj.EndPt[0]);
847  SetEndPoints(tjs, tj);
848  }
849 
850  if(prt) mf::LogVerbatim("TC")<<"ReversePropagate: Prepping Tj "<<tj.ID<<" incoming StepDir "<<tj.StepDir;
851 
852  short stepDir = tj.StepDir;
853 
854  // find the wire on which EndPt resides
855  unsigned int wire0 = std::nearbyint(tj.Pts[0].Pos[0]);
856  unsigned int nextWire = wire0 - tj.StepDir;
857 
858  // check for dead wires
859  geo::PlaneID planeID = DecodeCTP(tj.CTP);
860  unsigned short ipl = planeID.Plane;
861  while(nextWire > tjs.FirstWire[ipl] && nextWire < tjs.LastWire[ipl]) {
862  if(tjs.WireHitRange[ipl][nextWire].first >= 0) break;
863  nextWire -= tj.StepDir;
864  }
865  if(nextWire == tjs.LastWire[ipl] - 1) return;
866  // clone the first point
867  TrajPoint tp = tj.Pts[0];
868  // strip off the hits
869  tp.Hits.clear(); tp.UseHit.reset();
870  // move it to the next wire
871  MoveTPToWire(tp, (float)nextWire);
872  // find close unused hits near this position
873  float maxDelta = 10 * tj.Pts[tj.EndPt[1]].DeltaRMS;
874  if(!FindCloseHits(tjs, tp, maxDelta, kUnusedHits)) return;
875  if(prt) mf::LogVerbatim("TC")<<" nUnused hits "<<tp.Hits.size()<<" at Pos "<<PrintPos(tjs, tp);
876  if(tp.Hits.empty()) return;
877  // There are hits on the next wire. Make a copy, reverse it and try
878  // to extend it with StepCrawl
879  if(prt) {
880  mf::LogVerbatim myprt("TC");
881  myprt<<" tp.Hits ";
882  for(auto& iht : tp.Hits) myprt<<" "<<PrintHit(tjs.fHits[iht])<<"_"<<tjs.fHits[iht].InTraj;
883  } // prt
884  //
885  // Make a working copy of tj
886  Trajectory tjWork = tj;
887  // So the first shall be last and the last shall be first
888  ReverseTraj(tjs, tjWork);
889  // Flag it to use special cuts in StepCrawl
890  tjWork.AlgMod[kRvPrp] = true;
891  // We are doing this probably because the trajectory is stopping.
892  // Reduce the number of fitted points to a small number
893  unsigned short lastPt = tjWork.Pts.size() - 1;
894  if(lastPt < 4) return;
895  // update the charge
896  float chg = 0;
897  float cnt = 0;
898  for(unsigned short ii = 0; ii < 4; ++ii) {
899  unsigned short ipt = lastPt - ii;
900  if(tjWork.Pts[ipt].Chg == 0) continue;
901  chg += tjWork.Pts[ipt].Chg;
902  ++cnt;
903  } // ii
904  if(cnt == 0) return;
905  if(cnt > 1) tjWork.Pts[lastPt].AveChg = chg / cnt;
906  StepCrawl(tjWork);
907  if(!fGoodTraj) {
908  if(prt) mf::LogVerbatim("TC")<<" ReversePropagate StepCrawl failed";
909  return;
910  }
911  // check the new stopping point
912  ChkStopEndPts(tjWork, prt);
913  // restore the original direction
914  if(tjWork.StepDir != stepDir) ReverseTraj(tjs, tjWork);
915  tj = tjWork;
916  // re-check the ends
917  ChkStop(tj);
918  if(prt) {
919  mf::LogVerbatim("TC")<<" ReversePropagate success. Outgoing StepDir "<<tj.StepDir;
920  if(tj.Pts.size() < 50) PrintTrajectory("RP", tjs, tj, USHRT_MAX);
921  }
922 
923  } // ReversePropagate
924 
927  {
928  // Makes junk trajectories using unassigned hits
929 
930  // shouldn't have to do this but...
931  for(unsigned int iht = 0; iht < tjs.fHits.size(); ++iht) {
932  if(tjs.fHits[iht].InTraj < 0) {
933  std::cout<<"FJT: dirty hit "<<PrintHit(tjs.fHits[iht])<<"\n";
934  tjs.fHits[iht].InTraj = 0;
935  }
936  }
937  unsigned short plane = DecodeCTP(inCTP).Plane;
938  std::vector<unsigned int> tHits;
939  bool jtPrt = false;
940  for(unsigned int iwire = tjs.FirstWire[plane]; iwire < tjs.LastWire[plane] - 1; ++iwire) {
941  // skip bad wires or no hits on the wire
942  if(tjs.WireHitRange[plane][iwire].first < 0) continue;
943  unsigned int jwire = iwire + 1;
944  if(tjs.WireHitRange[plane][jwire].first < 0) continue;
945  unsigned int ifirsthit = (unsigned int)tjs.WireHitRange[plane][iwire].first;
946  unsigned int ilasthit = (unsigned int)tjs.WireHitRange[plane][iwire].second;
947  unsigned int jfirsthit = (unsigned int)tjs.WireHitRange[plane][jwire].first;
948  unsigned int jlasthit = (unsigned int)tjs.WireHitRange[plane][jwire].second;
949  for(unsigned int iht = ifirsthit; iht < ilasthit; ++iht) {
950  jtPrt = (iht == debug.Hit);
951  if(jtPrt) {
952  mf::LogVerbatim("TC")<<"FindJunkTraj: Found debug hit "<<PrintHit(tjs.fHits[iht])<<" fJTMaxHitSep2 "<<fJTMaxHitSep2<<" iht "<<iht<<" jfirsthit "<<jfirsthit<<" jlasthit "<<jlasthit;
953  }
954  if(tjs.fHits[iht].InTraj != 0) continue;
955  std::vector<unsigned int> iHits;
956  GetHitMultiplet(iht, iHits);
957  for(unsigned int jht = jfirsthit; jht < jlasthit; ++jht) {
958  if(tjs.fHits[jht].InTraj != 0) continue;
959  if(prt && HitSep2(tjs, iht, jht) < 100) mf::LogVerbatim("TC")<<" use "<<PrintHit(tjs.fHits[jht]);
960  if(HitSep2(tjs, iht, jht) > fJTMaxHitSep2) continue;
961  std::vector<unsigned int> jHits;
962  GetHitMultiplet(jht, jHits);
963  // check for hit overlap consistency
964  if(!TrajHitsOK(tjs, iHits, jHits)) continue;
965  tHits.clear();
966  // add the available hits and flag them
967  for(auto iht : iHits) if(tjs.fHits[iht].InTraj == 0) tHits.push_back(iht);
968  for(auto jht : jHits) if(tjs.fHits[jht].InTraj == 0) tHits.push_back(jht);
969  for(auto tht : tHits) tjs.fHits[tht].InTraj = -4;
970  unsigned int loWire, hiWire;
971  if(iwire != 0) { loWire = iwire - 1; } else { loWire = 0; }
972  if(jwire < tjs.NumWires[plane] - 3) { hiWire = jwire + 2; } else { hiWire = tjs.NumWires[plane] - 1; }
973  bool hitsAdded = true;
974  unsigned short nit = 0;
975  while(hitsAdded && nit < 100) {
976  hitsAdded = false;
977  for(unsigned int kwire = loWire; kwire < hiWire + 1; ++kwire) {
978  if(tjs.WireHitRange[plane][kwire].first < 0) continue;
979  unsigned int kfirsthit = (unsigned int)tjs.WireHitRange[plane][kwire].first;
980  unsigned int klasthit = (unsigned int)tjs.WireHitRange[plane][kwire].second;
981  for(unsigned int kht = kfirsthit; kht < klasthit; ++kht) {
982  if(tjs.fHits[kht].InTraj != 0) continue;
983  // this shouldn't be needed but do it anyway
984  if(std::find(tHits.begin(), tHits.end(), kht) != tHits.end()) continue;
985  // re-purpose jHits and check for consistency
986  GetHitMultiplet(kht, jHits);
987  if(!TrajHitsOK(tjs, tHits, jHits)) continue;
988  // add them all and update the wire range
989  for(auto jht : jHits) {
990  if(tjs.fHits[jht].InTraj != 0) continue;
991  tHits.push_back(jht);
992  tjs.fHits[jht].InTraj = -4;
993  if(kwire > hiWire) hiWire = kwire;
994  if(kwire < loWire) loWire = kwire;
995  hitsAdded = true;
996  } // jht
997  } // kht
998 // if(jtPrt) mf::LogVerbatim("TC")<<" kwire "<<kwire<<" thits size "<<tHits.size();
999  } // kwire
1000  ++nit;
1001  } // hitsAdded && nit < 100
1002  // clear InTraj
1003  for(auto iht : tHits) tjs.fHits[iht].InTraj = 0;
1004  if(jtPrt) {
1005  mf::LogVerbatim myprt("TC");
1006  myprt<<"FJT: tHits";
1007  for(auto tht : tHits) myprt<<" "<<PrintHit(tjs.fHits[tht]);
1008  myprt<<"\n";
1009  } // prt
1010  // See if this is a ghost trajectory
1011  unsigned short ofTraj = USHRT_MAX;
1012  if(IsGhost(tHits, ofTraj)) {
1013  if(jtPrt) mf::LogVerbatim()<<"FJT: Is ghost of "<<ofTraj;
1014  break;
1015  }
1016  if(!MakeJunkTraj(tHits, jtPrt)) {
1017  if(jtPrt) mf::LogVerbatim()<<"FJT: MakeJunkTraj failed";
1018  break;
1019  }
1020  if(tjs.fHits[jht].InTraj > 0) break;
1021  } // jht
1022  } // iht
1023  } // iwire
1024  } // FindJunkTraj
1025 
1027  bool TrajClusterAlg::MakeJunkTraj(std::vector<unsigned int> tHits, bool jtPrt)
1028  {
1029 
1030  if(!tjs.UseAlg[kJunkTj]) return false;
1031  // Make a crummy trajectory using the provided hits
1032 
1033  if(tHits.size() < 2) return false;
1034 
1035  std::vector<std::vector<unsigned int>> tpHits;
1036  unsigned short ii, iht, ipt;
1037 
1038  // Start the trajectory using the first and last hits to
1039  // define a starting direction. Use the last pass settings
1040  Trajectory work;
1041  unsigned short pass = fMinPts.size() - 1;
1042  if(!StartTraj(work, tHits[0], tHits[tHits.size()-1], pass)) return false;
1043 
1044  // Make TPs with the same separation as the wire spacing
1045  constexpr float pointSize = 1;
1046 
1047  // Do a more detailed specification of TPs if there
1048  // are enough hits
1049  if(tHits.size() > 6) {
1050  // fit all of the hits to a line
1051  std::vector<float> x(tHits.size()), y(tHits.size()), yerr2(tHits.size(), 1000.);
1052  float intcpt, slope, intcpterr, slopeerr, chidof;
1053 
1054  for(ii = 0; ii < tHits.size(); ++ii) {
1055  iht = tHits[ii];
1056  if(tjs.fHits[iht].InTraj == INT_MAX) return false;
1057  x[ii] = tjs.fHits[iht].ArtPtr->WireID().Wire;
1058  y[ii] = tjs.fHits[iht].PeakTime * tjs.UnitsPerTick;
1059  } // ii
1060  fLinFitAlg.LinFit(x, y, yerr2, intcpt, slope, intcpterr, slopeerr, chidof);
1061 
1062  if(jtPrt) mf::LogVerbatim("TC")<<" tHits line fit chidof "<<chidof<<" Angle "<<atan(slope);
1063  // return without making a junk trajectory
1064  if(chidof == 999.) return false;
1065  // A rough estimate of the trajectory angle
1066  work.Pts[0].Ang = atan(slope);
1067  work.Pts[0].Dir[0] = cos(work.Pts[0].Ang);
1068  work.Pts[0].Dir[1] = sin(work.Pts[0].Ang);
1069  SetAngleCode(tjs, work.Pts[0]);
1070  // Rotate the hits into this coordinate system to find the start and end
1071  // points and general direction
1072  double cs = cos(-work.Pts[0].Ang);
1073  double sn = sin(-work.Pts[0].Ang);
1074  float tAlong, minAlong = 1E6, maxAlong = -1E6;
1075  // sort the hits by the distance along the general direction
1076  std::vector<SortEntry> sortVec(tHits.size());
1077  SortEntry sortEntry;
1078  for(ii = 0; ii < x.size(); ++ii) {
1079  tAlong = cs * x[ii] - sn * y[ii];
1080  if(tAlong < minAlong) minAlong = tAlong;
1081  if(tAlong > maxAlong) maxAlong = tAlong;
1082  sortEntry.index = ii;
1083  sortEntry.val = tAlong;
1084  sortVec[ii] = sortEntry;
1085  } // ii
1086  std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
1087  // make a temp vector
1088  std::vector<unsigned int> tmp(sortVec.size());
1089  // overwrite with the sorted values
1090  for(ii = 0; ii < sortVec.size(); ++ii) tmp[ii] = tHits[sortVec[ii].index];
1091  tHits = tmp;
1092  // create a trajectory point at each WSE unit (if there are hits at that point)
1093  unsigned short npts = (unsigned short)((maxAlong - minAlong) / pointSize);
1094  // rotate back into normal coordinate system
1095  if(jtPrt) mf::LogVerbatim("TC")<<" minAlong "<<minAlong<<" maxAlong "<<maxAlong<<" work.Pts[0].Ang "<<work.Pts[0].Ang<<" npts "<<npts;
1096  if(npts < 2) npts = 2;
1097  tpHits.resize(npts);
1098  for(ii = 0; ii < tHits.size(); ++ii) {
1099  ipt = (unsigned short)((sortVec[ii].val - minAlong) / pointSize);
1100  if(ipt > npts - 1) ipt = npts - 1;
1101  if(jtPrt) mf::LogVerbatim("TC")<<"tHit "<<PrintHit(tjs.fHits[tHits[ii]])<<" length "<<sortVec[ii].val<<" ipt "<<ipt<<" Chg "<<(int)tjs.fHits[tHits[ii]].Integral;
1102  if(tpHits[ipt].size() < 16) tpHits[ipt].push_back(tHits[ii]);
1103  }
1104  } else {
1105  // just a few hits. Put each one at a TP in the order that
1106  // they were found
1107  tpHits.resize(tHits.size());
1108  for(ii = 0; ii < tHits.size(); ++ii) {
1109  tpHits[ii].push_back(tHits[ii]);
1110  }
1111  } // tHits.size()
1112  // make the TPs
1113  // work.Pts[0] is already defined but it needs hits added
1114  work.Pts[0].Hits = tpHits[0];
1115  for(auto iht : tpHits[0]) tjs.fHits[iht].InTraj = work.ID;
1116  // set all bits true
1117  work.Pts[0].UseHit.set();
1118  DefineHitPos(work.Pts[0]);
1119  work.Pts[0].Pos = work.Pts[0].HitPos;
1120  if(jtPrt) PrintTrajectory("MJT", tjs, work, USHRT_MAX);
1121  // another TP to get the direction
1122  TrajPoint tpd;
1123  // make the rest of the TPs
1124  for(ipt = 1; ipt < tpHits.size(); ++ipt) {
1125  if(tpHits[ipt].empty()) continue;
1126  // Use the previous TP as a starting point
1127  unsigned short lastPt = work.Pts.size() - 1;
1128  TrajPoint tp = work.Pts[lastPt];
1129  tp.Step = ipt;
1130  tp.Hits = tpHits[ipt];
1131  for(auto iht : tpHits[ipt]) tjs.fHits[iht].InTraj = work.ID;
1132  // use all hits
1133  tp.UseHit.set();
1134  DefineHitPos(tp);
1135  // Just use the hit position as the tj position
1136  tp.Pos = tp.HitPos;
1137  if(TrajPointSeparation(work.Pts[ipt-1], tp) < 0.5) {
1138  for(auto iht : tpHits[ipt]) tjs.fHits[iht].InTraj = 0;
1139  continue;
1140  }
1141  work.Pts.push_back(tp);
1142  SetEndPoints(tjs, work);
1143  }
1144  if(jtPrt) {
1145  PrintTrajectory("MJT", tjs, work, USHRT_MAX);
1146  }
1147  work.AlgMod[kJunkTj] = true;
1148  fGoodTraj = true;
1149  // Finally push it onto tjs.allTraj
1150  fQuitAlg = !StoreTraj(tjs, work);
1151  if(fQuitAlg) {
1152  ReleaseHits(tjs, work);
1153  return false;
1154  }
1155  if(tjs.UseAlg[kChkInTraj]) {
1156  fQuitAlg = !InTrajOK(tjs, "MJT");
1157  if(fQuitAlg) {
1158  ReleaseHits(tjs, work);
1159  mf::LogVerbatim("TC")<<"InTrajOK failed in MakeJunkTraj";
1160  return false;
1161  }
1162  }
1163  return true;
1164  } // MakeJunkTraj
1165 
1167  void TrajClusterAlg::AddLAHits(Trajectory& tj, unsigned short ipt, bool& sigOK)
1168  {
1169  // Very Large Angle version of AddHits to be called for the last angle range
1170 
1171  if(ipt > tj.Pts.size() - 1) return;
1172  TrajPoint& tp = tj.Pts[ipt];
1173  tp.Hits.clear();
1174  tp.UseHit.reset();
1175  sigOK = false;
1176 
1177  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1178 
1179  // look at adjacent wires for larger angle trajectories
1180  // We will check the most likely wire first
1181  std::vector<int> wires(1);
1182  wires[0] = std::nearbyint(tp.Pos[0]);
1183  if(wires[0] < 0 || wires[0] > (int)tjs.LastWire[plane]-1) return;
1184 
1185  if(tp.AngleCode != 2) {
1186  mf::LogVerbatim("TC")<<"AddLAHits called with a bad angle code. "<<tp.AngleCode<<" Don't do this";
1187  return;
1188  }
1189  // and the adjacent wires next in the most likely order only
1190  // after the first two points have been defined
1191  if(ipt > 1) {
1192  if(tp.Dir[0] > 0) {
1193  if(wires[0] < (int)tjs.LastWire[plane]-1) wires.push_back(wires[0] + 1);
1194  if(wires[0] > 0) wires.push_back(wires[0] - 1);
1195  } else {
1196  if(wires[0] > 0) wires.push_back(wires[0] - 1);
1197  if(wires[0] < (int)tjs.LastWire[plane]-1) wires.push_back(wires[0] + 1);
1198  }
1199  } // ipt > 0 ...
1200 
1201  if(prt) {
1202  mf::LogVerbatim myprt("TC");
1203  myprt<<" AddLAHits: Pos "<<PrintPos(tjs, tp)<<" tp.AngleCode "<<tp.AngleCode<<" Wires under consideration";
1204  for(auto& wire : wires) myprt<<" "<<wire;
1205  }
1206 
1207  // a temporary tp that we can move around
1208  TrajPoint ltp = tp;
1209  // do this while testing
1210  sigOK = false;
1211 
1212  tp.Hits.clear();
1213  std::array<int, 2> wireWindow;
1214  std::array<float, 2> timeWindow;
1215  float pos1Window = fVLAStepSize/2;
1216  timeWindow[0] = ltp.Pos[1] - pos1Window;
1217  timeWindow[1] = ltp.Pos[1] + pos1Window;
1218  // Put the existing hits in to a vector so we can ensure that they aren't added again
1219  std::vector<unsigned int> oldHits = PutTrajHitsInVector(tj, kAllHits);
1220 
1221  for(unsigned short ii = 0; ii < wires.size(); ++ii) {
1222  int wire = wires[ii];
1223  if(wire < 0 || wire > (int)tjs.LastWire[plane]) continue;
1224  // Assume a signal exists on a dead wire
1225  if(tjs.WireHitRange[plane][wire].first == -1) sigOK = true;
1226  if(tjs.WireHitRange[plane][wire].first < 0) continue;
1227  wireWindow[0] = wire;
1228  wireWindow[1] = wire;
1229  bool hitsNear;
1230  // Look for hits using the requirement that the timeWindow overlaps with the hit StartTick and EndTick
1231  std::vector<unsigned int> closeHits = FindCloseHits(tjs, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
1232  if(hitsNear) sigOK = true;
1233  for(auto& iht : closeHits) {
1234  // Ensure that none of these hits are already used by this trajectory
1235  if(tjs.fHits[iht].InTraj == tj.ID) continue;
1236  // or in another trajectory in any previously added point
1237  if(std::find(oldHits.begin(), oldHits.end(), iht) != oldHits.end()) continue;
1238  tp.Hits.push_back(iht);
1239  }
1240  } // ii
1241 
1242  if(prt) {
1243  mf::LogVerbatim myprt("TC");
1244  myprt<<" LAPos "<<PrintPos(tjs, ltp)<<" Tick window "<<(int)(timeWindow[0]/tjs.UnitsPerTick)<<" to "<<(int)(timeWindow[1]/tjs.UnitsPerTick);
1245  for(auto& iht : tp.Hits) myprt<<" "<<PrintHit(tjs.fHits[iht]);
1246  } // prt
1247 
1248  // no hits found
1249  if(tp.Hits.empty()) return;
1250 
1251  if(tp.Hits.size() > 16) tp.Hits.resize(16);
1252 
1253  tp.UseHit.reset();
1254 
1255  if(tjs.UseAlg[kStopAtTj]) {
1256  // don't continue if we have run into another trajectory that has a similar angle
1257  unsigned short nAvailable = 0;
1258  unsigned int otherTjHit = INT_MAX;
1259  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1260  auto& hit = tjs.fHits[tp.Hits[ii]];
1261  if(hit.InTraj == INT_MAX) continue;
1262  if(hit.InTraj > 0) {
1263  otherTjHit = tp.Hits[ii];
1264  continue;
1265  }
1266  ++nAvailable;
1267  } // ii
1268  if(nAvailable == 0 && otherTjHit != UINT_MAX) {
1269  // get the trajectory index
1270  unsigned short otherTj = tjs.fHits[otherTjHit].InTraj - 1;
1271  Trajectory& otj = tjs.allTraj[otherTj];
1272  // find out what point the hit is in
1273  unsigned short atPt = USHRT_MAX;
1274  for(unsigned short ipt = 0; ipt < otj.Pts.size(); ++ipt) {
1275  for(auto& iht : otj.Pts[ipt].Hits) {
1276  if(iht == otherTjHit) {
1277  atPt = ipt;
1278  break;
1279  } // iht == otherTjHit
1280  } // iht
1281  if(atPt != USHRT_MAX) break;
1282  } // ipt
1283  if(atPt != USHRT_MAX && DeltaAngle(tp.Ang, otj.Pts[atPt].Ang) < 0.1) {
1284  if(prt) mf::LogVerbatim("TC")<<" Found a VLA merge candidate trajectory "<<otj.ID<<". Set StopFlag[kAtTj] and stop stepping";
1285  tj.StopFlag[1][kAtTj] = true;
1286  return;
1287  } // atPt is valid
1288  } // nAvailable == 0 &&
1289  } // stop at Tj
1290 
1291  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1292  unsigned int iht = tp.Hits[ii];
1293  if(tjs.fHits[iht].InTraj != 0) continue;
1294  tp.UseHit[ii] = true;
1295  tjs.fHits[iht].InTraj = tj.ID;
1296  } // ii
1297  DefineHitPos(tp);
1298  SetEndPoints(tjs, tj);
1299  UpdateTjChgProperties("ALAH", tjs, tj, prt);
1300 
1301  } // AddLAHits
1302 
1304  void TrajClusterAlg::AddHits(Trajectory& tj, unsigned short ipt, bool& sigOK)
1305  {
1306  // Try to add hits to the trajectory point ipt on the supplied
1307  // trajectory
1308 
1309  // assume failure
1310  sigOK = false;
1311 
1312  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1313 
1314  if(tj.Pts.empty()) return;
1315  if(ipt > tj.Pts.size() - 1) return;
1316 
1317  // Call large angle hit finding if the last tp is large angle
1318  if(tj.Pts[ipt].AngleCode == 2) {
1319  AddLAHits(tj, ipt, sigOK);
1320  return;
1321  }
1322  std::vector<unsigned int> closeHits;
1323 
1324  unsigned int lastPtWithUsedHits = tj.EndPt[1];
1325  TrajPoint& tp = tj.Pts[ipt];
1326 
1327  unsigned int wire = std::nearbyint(tp.Pos[0]);
1328  if(wire < tjs.FirstWire[plane] || wire > tjs.LastWire[plane]-1) return;
1329  // Move the TP to this wire
1330  MoveTPToWire(tp, (float)wire);
1331 
1332  // find the projection error to this point. Note that if this is the first
1333  // TP, lastPtWithUsedHits = 0, so the projection error is 0
1334  float dw = tp.Pos[0] - tj.Pts[lastPtWithUsedHits].Pos[0];
1335  float dt = tp.Pos[1] - tj.Pts[lastPtWithUsedHits].Pos[1];
1336  float dpos = sqrt(dw * dw + dt * dt);
1337  float projErr = dpos * tj.Pts[lastPtWithUsedHits].AngErr;
1338  // Add this to the Delta RMS factor and construct a cut
1339  float deltaCut = 3 * (projErr + tp.DeltaRMS);
1340 
1341  deltaCut *= fProjectionErrFactor;
1342  if(prt) mf::LogVerbatim("TC")<<" AddHits: calculated deltaCut "<<deltaCut;
1343 
1344 // if(deltaCut < 2) deltaCut = 2;
1345  // Jan 26 Cut is too loose
1346  if(deltaCut < 0.5) deltaCut = 0.5;
1347  if(deltaCut > 3) deltaCut = 3;
1348 
1349  // TY: open it up for RevProp, since we might be following a stopping track
1350  if(tj.AlgMod[kRvPrp]) deltaCut *= 2;
1351 
1352  // loosen up a bit if we just passed a block of dead wires
1353  if(abs(dw) > 20 && DeadWireCount(tjs, tp.Pos[0], tj.Pts[lastPtWithUsedHits].Pos[0], tj.CTP) > 10) deltaCut *= 2;
1354 
1355  // Create a larger cut to use in case there is nothing close
1356  float bigDelta = 2 * deltaCut;
1357  unsigned int imBig = UINT_MAX;
1358  tp.Delta = deltaCut;
1359  // ignore all hits with delta larger than maxDeltaCut
1360  float maxDeltaCut = 2 * bigDelta;
1361 
1362  // projected time in ticks for testing the existence of a hit signal
1363  raw::TDCtick_t rawProjTick = (float)(tp.Pos[1] / tjs.UnitsPerTick);
1364  if(prt) {
1365  mf::LogVerbatim("TC")<<" AddHits: wire "<<wire<<" tp.Pos[0] "<<tp.Pos[0]<<" projTick "<<rawProjTick<<" deltaRMS "<<tp.DeltaRMS<<" tp.Dir[0] "<<tp.Dir[0]<<" deltaCut "<<deltaCut<<" dpos "<<dpos<<" projErr "<<projErr<<" ExpectedHitsRMS "<<ExpectedHitsRMS(tjs, tp);
1366  }
1367 
1368  std::vector<unsigned int> hitsInMultiplet;
1369 
1370  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1371  unsigned int ipl = planeID.Plane;
1372  if(wire > tjs.LastWire[ipl]) return;
1373  // Assume a signal exists on a dead wire
1374  if(tjs.WireHitRange[ipl][wire].first == -1) sigOK = true;
1375  if(tjs.WireHitRange[ipl][wire].first < 0) return;
1376  unsigned int firstHit = (unsigned int)tjs.WireHitRange[ipl][wire].first;
1377  unsigned int lastHit = (unsigned int)tjs.WireHitRange[ipl][wire].second;
1378  float fwire = wire;
1379  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
1380  auto& hit = tjs.fHits[iht];
1381  if(hit.InTraj == tj.ID) continue;
1382  if(hit.InTraj == INT_MAX) continue;
1383  if(rawProjTick > hit.StartTick && rawProjTick < hit.EndTick) sigOK = true;
1384  float ftime = tjs.UnitsPerTick * hit.PeakTime;
1385  float delta = PointTrajDOCA(tjs, fwire, ftime, tp);
1386  if(delta > maxDeltaCut) continue;
1387  float dt = std::abs(ftime - tp.Pos[1]);
1388  unsigned short localIndex = 0;
1389  GetHitMultiplet(iht, hitsInMultiplet, localIndex);
1390  if(prt && delta < 100 && dt < 100) {
1391  mf::LogVerbatim myprt("TC");
1392  myprt<<" iht "<<iht;
1393  myprt<<" "<<hit.ArtPtr->WireID().Plane<<":"<<PrintHit(hit);
1394  myprt<<" delta "<<std::fixed<<std::setprecision(2)<<delta<<" deltaCut "<<deltaCut<<" dt "<<dt;
1395  myprt<<" BB Mult "<<hitsInMultiplet.size()<<" localIndex "<<localIndex<<" RMS "<<std::setprecision(1)<<hit.RMS;
1396  myprt<<" Chi "<<std::setprecision(1)<<hit.GoodnessOfFit;
1397  myprt<<" InTraj "<<hit.InTraj;
1398  myprt<<" Chg "<<(int)hit.Integral;
1399  myprt<<" mcpIndex "<<hit.MCPartListIndex;
1400  myprt<<" Signal? "<<sigOK;
1401  }
1402  if(hit.InTraj == 0 && delta < bigDelta && hitsInMultiplet.size() < 3 && !tj.AlgMod[kRvPrp]) {
1403  // An available hit that is just outside the window that is not part of a large multiplet
1404  bigDelta = delta;
1405  imBig = iht;
1406  }
1407  if(delta > deltaCut) continue;
1408  if(std::find(closeHits.begin(), closeHits.end(), iht) != closeHits.end()) continue;
1409  closeHits.push_back(iht);
1410  if(hitsInMultiplet.size() > 1) {
1411  // include all the hits in a multiplet
1412  for(auto& jht : hitsInMultiplet) {
1413  if(tjs.fHits[jht].InTraj == tj.ID) continue;
1414  if(std::find(closeHits.begin(), closeHits.end(), jht) != closeHits.end()) continue;
1415  closeHits.push_back(jht);
1416  } // jht
1417  } // multiplicity > 1
1418  } // iht
1419 
1420  if(prt) {
1421  mf::LogVerbatim myprt("TC");
1422  myprt<<"closeHits ";
1423  for(auto iht : closeHits) myprt<<" "<<PrintHit(tjs.fHits[iht]);
1424  if(imBig < tjs.fHits.size()) {
1425  myprt<<" imBig "<<PrintHit(tjs.fHits[imBig]);
1426  } else {
1427  myprt<<" imBig "<<imBig;
1428  }
1429  }
1430  if(closeHits.empty() && imBig == UINT_MAX) {
1431  if(prt) mf::LogVerbatim("TC")<<" no signal on any wire at tp.Pos "<<tp.Pos[0]<<" "<<tp.Pos[1]<<" tick "<<(int)tp.Pos[1]/tjs.UnitsPerTick<<" closeHits size "<<closeHits.size();
1432  return;
1433  }
1434  if(imBig < tjs.fHits.size() && closeHits.empty()) {
1435  closeHits.push_back(imBig);
1436  if(prt) mf::LogVerbatim("TC")<<" Added bigDelta hit "<<PrintHit(tjs.fHits[imBig])<<" w delta = "<<bigDelta;
1437  }
1438  if(!closeHits.empty()) sigOK = true;
1439  if(!sigOK) return;
1440  tp.Hits.insert(tp.Hits.end(), closeHits.begin(), closeHits.end());
1441  if(tp.Hits.size() > 16) {
1442  // Actually this is a hopelessly messy region that we should ignore
1443  tp.Hits.clear();
1444  tp.Chg = 0;
1445  return;
1446  }
1447  // reset UseHit and assume that none of these hits will be used (yet)
1448  tp.UseHit.reset();
1449  // decide which of these hits should be used in the fit. Use a generous maximum delta
1450  // and require a charge check if we'not just starting out
1451  bool useChg = true;
1452  FindUseHits(tj, ipt, 10, useChg);
1453  DefineHitPos(tp);
1454  SetEndPoints(tjs, tj);
1455  if(prt) mf::LogVerbatim("TC")<<" number of close hits "<<closeHits.size()<<" used hits "<<NumHitsInTP(tp, kUsedHits);
1456  } // AddHits
1457 
1459  void TrajClusterAlg::FindUseHits(Trajectory& tj, unsigned short ipt, float maxDelta, bool useChg)
1460  {
1461  // Hits have been associated with trajectory point ipt but none are used. Here we will
1462  // decide which hits to use.
1463 
1464  if(ipt > tj.Pts.size() - 1) return;
1465  TrajPoint& tp = tj.Pts[ipt];
1466 
1467  if(tp.Hits.empty()) return;
1468 
1469  // Use all available hits on the last pass for the first few points when starting out
1470  if(ipt < 3 && tj.Pass == fMinPts.size() - 1) {
1471  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1472  unsigned int iht = tp.Hits[ii];
1473  if(tjs.fHits[iht].InTraj > 0) continue;
1474  tp.UseHit[ii] = true;
1475  tjs.fHits[iht].InTraj = tj.ID;
1476  }
1477  if(prt) mf::LogVerbatim("TC")<<"FUH: Using all hits on seed trajectory on the last pass";
1478  return;
1479  } // last pass for the first two points
1480 
1481  // don't check charge when starting out
1482  if(ipt < 5) useChg = false;
1483  float chgPullCut = 1000;
1484  if(useChg) chgPullCut = tjs.ChargeCuts[0];
1485  // open it up for RevProp, since we might be following a stopping track
1486  if(tj.AlgMod[kRvPrp]) chgPullCut *= 2;
1487  // BB April 19, 2018: open it up for low MCSMom tjs
1488  if(tj.MCSMom < 30) chgPullCut *= 2;
1489 
1490  if(prt) {
1491  mf::LogVerbatim("TC")<<"FUH: maxDelta "<<maxDelta<<" useChg requested "<<useChg<<" Norm AveChg "<<(int)tp.AveChg<<" tj.ChgRMS "<<std::setprecision(2)<<tj.ChgRMS<<" chgPullCut "<<chgPullCut<<" TPHitsRMS "<<(int)TPHitsRMSTick(tjs, tp, kUnusedHits)<<" ExpectedHitsRMS "<<(int)ExpectedHitsRMS(tjs, tp)<<" AngCode "<<tp.AngleCode;
1492  }
1493 
1494  // inverse of the path length for normalizing hit charge to 1 WSE unit
1495  float pathInv = std::abs(tp.Dir[0]);
1496  if(pathInv < 0.05) pathInv = 0.05;
1497 
1498  // Find the hit that has the smallest delta and the number of available hits
1499  tp.Delta = maxDelta;
1500  float delta;
1501  unsigned short imbest = USHRT_MAX;
1502  std::vector<float> deltas(tp.Hits.size(), 100);
1503  // keep track of the best delta - even if it is used
1504  float bestDelta = maxDelta;
1505  unsigned short nAvailable = 0;
1506  unsigned short firstAvailable = USHRT_MAX;
1507  unsigned short lastAvailable = USHRT_MAX;
1508  unsigned short firstUsed = USHRT_MAX;
1509  unsigned short imBadRecoHit = USHRT_MAX;
1510  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1511  tp.UseHit[ii] = false;
1512  unsigned int iht = tp.Hits[ii];
1513  delta = PointTrajDOCA(tjs, iht, tp);
1514  if(delta < bestDelta) bestDelta = delta;
1515  if(tjs.fHits[iht].InTraj > 0) {
1516  if(firstUsed == USHRT_MAX) firstUsed = ii;
1517  continue;
1518  }
1519  if(tjs.fHits[iht].GoodnessOfFit < 0 || tjs.fHits[iht].GoodnessOfFit > 100) imBadRecoHit = ii;
1520  if(firstAvailable == USHRT_MAX) firstAvailable = ii;
1521  lastAvailable = ii;
1522  ++nAvailable;
1523  if(prt) {
1524  if(useChg) {
1525  if(prt) mf::LogVerbatim("TC")<<" "<<ii<<" "<<PrintHit(tjs.fHits[iht])<<" delta "<<delta<<" Norm Chg "<<(int)(tjs.fHits[iht].Integral * pathInv);
1526  } else {
1527  if(prt) mf::LogVerbatim("TC")<<" "<<ii<<" "<<PrintHit(tjs.fHits[iht])<<" delta "<<delta;
1528  }
1529  } // prt
1530  deltas[ii] = delta;
1531  if(delta < tp.Delta) {
1532  tp.Delta = delta;
1533  imbest = ii;
1534  }
1535  } // ii
1536 
1537  float chgWght = 0.5;
1538 
1539  if(prt) mf::LogVerbatim("TC")<<" firstAvailable "<<firstAvailable<<" lastAvailable "<<lastAvailable<<" firstUsed "<<firstUsed<<" imbest "<<imbest<<" single hit. tp.Delta "<<std::setprecision(2)<<tp.Delta<<" bestDelta "<<bestDelta<<" path length "<<1 / pathInv<<" imBadRecoHit "<<imBadRecoHit;
1540  if(imbest == USHRT_MAX || nAvailable == 0) return;
1541  unsigned int bestDeltaHit = tp.Hits[imbest];
1542 
1543  // Don't try to use a multiplet if a hit in the middle is in a different trajectory
1544  // March 11: Fix the logic
1545  if(tp.Hits.size() > 2 && nAvailable > 1 && firstUsed != USHRT_MAX && firstAvailable < firstUsed && lastAvailable > firstUsed) {
1546  if(prt) mf::LogVerbatim("TC")<<" A hit in the middle of the multiplet is used. Use only the best hit";
1547  tp.UseHit[imbest] = true;
1548  tjs.fHits[bestDeltaHit].InTraj = tj.ID;
1549  return;
1550  } // Used hit inside multiplet
1551 
1552  if(tp.AngleCode == 1) {
1553  // Get the hits that are in the same multiplet as bestDeltaHit
1554  std::vector<unsigned int> hitsInMultiplet;
1555  unsigned short localIndex;
1556  GetHitMultiplet(bestDeltaHit, hitsInMultiplet, localIndex);
1557  if(prt) {
1558  mf::LogVerbatim myprt("TC");
1559  myprt<<" bestDeltaHit "<<PrintHit(tjs.fHits[bestDeltaHit]);
1560  myprt<<" in multiplet:";
1561  for(auto& iht : hitsInMultiplet) myprt<<" "<<PrintHit(tjs.fHits[iht]);
1562  }
1563  // Consider the case where a bad reco hit might be better. It is probably wider and
1564  // has more charge
1565  if(imBadRecoHit != USHRT_MAX) {
1566  unsigned int iht = tp.Hits[imBadRecoHit];
1567  if(tjs.fHits[iht].RMS > HitsRMSTick(tjs, hitsInMultiplet, kUnusedHits)) {
1568  if(prt) mf::LogVerbatim("TC")<<" Using imBadRecoHit "<<PrintHit(tjs.fHits[iht]);
1569  tp.UseHit[imBadRecoHit] = true;
1570  tjs.fHits[iht].InTraj = tj.ID;
1571  return;
1572  }
1573  } // bad reco hit
1574  // Use the hits in the multiplet instead
1575  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1576  unsigned int iht = tp.Hits[ii];
1577  if(tjs.fHits[iht].InTraj > 0) continue;
1578  // March 11
1579 // if(std::find(hitsInMultiplet.begin(), hitsInMultiplet.end(), iht) == hitsInMultiplet.end()) continue;
1580  tp.UseHit[ii] = true;
1581  tjs.fHits[iht].InTraj = tj.ID;
1582  } // ii
1583  return;
1584  } // isLA
1585 
1586  // don't use the best UNUSED hit if the best delta is for a USED hit and it is much better
1587  // TY: ignore for RevProp
1588  if(bestDelta < 0.5 * tp.Delta && !tj.AlgMod[kRvPrp]) return;
1589 
1590  if(!useChg || (useChg && (tj.AveChg <= 0 || tj.ChgRMS <= 0))) {
1591  // necessary quantities aren't available for more careful checking
1592  if(prt) mf::LogVerbatim("TC")<<" tj.AveChg "<<tj.AveChg<<" or tj.ChgRMS "<<tj.ChgRMS<<". Use the best hit";
1593  tp.UseHit[imbest] = true;
1594  tjs.fHits[bestDeltaHit].InTraj = tj.ID;
1595  return;
1596  }
1597 
1598  // Don't try to get fancy if we are tracking a long muon
1599  if(tj.PDGCode == 13 && bestDelta < 0.5) {
1600  if(prt) mf::LogVerbatim("TC")<<" Tracking muon. Use the best hit";
1601  tp.UseHit[imbest] = true;
1602  tjs.fHits[bestDeltaHit].InTraj = tj.ID;
1603  return;
1604  }
1605 
1606  // The best hit is the only one available or this is a small angle trajectory
1607  if(nAvailable == 1 || tp.AngleCode == 0) {
1608  float bestDeltaHitChgPull = std::abs(tjs.fHits[bestDeltaHit].Integral * pathInv / tp.AveChg - 1) / tj.ChgRMS;
1609  if(prt) mf::LogVerbatim("TC")<<" bestDeltaHitChgPull "<<bestDeltaHitChgPull<<" chgPullCut "<<chgPullCut;
1610  if(bestDeltaHitChgPull < chgPullCut || tp.Delta < 0.1) {
1611  tp.UseHit[imbest] = true;
1612  tjs.fHits[bestDeltaHit].InTraj = tj.ID;
1613  } // good charge or very good delta
1614  return;
1615  } // bestDeltaHitMultiplicity == 1
1616 
1617  // Find the expected width for the angle of this TP (ticks)
1618  float expectedWidth = ExpectedHitsRMS(tjs, tp);
1619 
1620  // Handle two available hits
1621  if(nAvailable == 2) {
1622  // See if these two are in the same multiplet and both are available
1623  std::vector<unsigned int> tHits;
1624  unsigned short localIndex;
1625  GetHitMultiplet(bestDeltaHit, tHits, localIndex);
1626  // ombest is the index of the other hit in tp.Hits that is in the same multiplet as bestDeltaHit
1627  // if we find it
1628  unsigned short ombest = USHRT_MAX;
1629  unsigned int otherHit = INT_MAX;
1630  if(tHits.size() == 2) {
1631  otherHit = tHits[1 - localIndex];
1632  // get the index of this hit in tp.Hits
1633  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1634  if(tjs.fHits[tp.Hits[ii]].InTraj > 0) continue;
1635  if(tp.Hits[ii] == otherHit) {
1636  ombest = ii;
1637  break;
1638  }
1639  } // ii
1640  } // tHits.size() == 2
1641  if(prt) {
1642  mf::LogVerbatim("TC")<<" Doublet: imbest "<<imbest<<" ombest "<<ombest;
1643  }
1644  // The other hit exists in the tp and it is available
1645  if(ombest < tp.Hits.size()) {
1646  // compare the best delta hit and the other hit separately and the doublet as a merged pair
1647  float bestHitDeltaErr = std::abs(tp.Dir[1]) * 0.17 + std::abs(tp.Dir[0]) * HitTimeErr(bestDeltaHit);
1648  // Construct a FOM starting with the delta pull
1649  float bestDeltaHitFOM = deltas[imbest] / bestHitDeltaErr;
1650  if(bestDeltaHitFOM < 0.5) bestDeltaHitFOM = 0.5;
1651  // multiply by the charge pull if it is significant
1652  float bestDeltaHitChgPull = std::abs(tjs.fHits[bestDeltaHit].Integral * pathInv / tp.AveChg - 1) / tj.ChgRMS;
1653  if(bestDeltaHitChgPull > 1) bestDeltaHitFOM *= chgWght * bestDeltaHitChgPull;
1654  // scale by the ratio
1655  float rmsRat = tjs.fHits[bestDeltaHit].RMS / expectedWidth;
1656  if(rmsRat < 1) rmsRat = 1 / rmsRat;
1657  bestDeltaHitFOM *= rmsRat;
1658  if(prt) mf::LogVerbatim("TC")<<" bestDeltaHit FOM "<<deltas[imbest]/bestHitDeltaErr<<" bestDeltaHitChgPull "<<bestDeltaHitChgPull<<" rmsRat "<<rmsRat<<" bestDeltaHitFOM "<<bestDeltaHitFOM;
1659  // Now do the same for the other hit
1660  float otherHitDeltaErr = std::abs(tp.Dir[1]) * 0.17 + std::abs(tp.Dir[0]) * HitTimeErr(otherHit);
1661  float otherHitFOM = deltas[ombest] / otherHitDeltaErr;
1662  if(otherHitFOM < 0.5) otherHitFOM = 0.5;
1663  float otherHitChgPull = std::abs(tjs.fHits[otherHit].Integral * pathInv / tp.AveChg - 1) / tj.ChgRMS;
1664  if(otherHitChgPull > 1) otherHitFOM *= chgWght * otherHitChgPull;
1665  rmsRat = tjs.fHits[otherHit].RMS / expectedWidth;
1666  if(rmsRat < 1) rmsRat = 1 / rmsRat;
1667  otherHitFOM *= rmsRat;
1668  if(prt) mf::LogVerbatim("TC")<<" otherHit FOM "<<deltas[ombest]/otherHitDeltaErr<<" otherHitChgPull "<<otherHitChgPull<<" rmsRat "<<rmsRat<<" otherHitFOM "<<otherHitFOM;
1669  // And for the doublet
1670  float doubletChg = tjs.fHits[bestDeltaHit].Integral + tjs.fHits[otherHit].Integral;
1671  float doubletTime = (tjs.fHits[bestDeltaHit].Integral * tjs.fHits[bestDeltaHit].PeakTime + tjs.fHits[otherHit].Integral * tjs.fHits[otherHit].PeakTime) / doubletChg;
1672  doubletChg *= pathInv;
1673  doubletTime *= tjs.UnitsPerTick;
1674  float doubletWidthTick = TPHitsRMSTick(tjs, tp, kUnusedHits);
1675  float doubletRMSTimeErr = doubletWidthTick * tjs.UnitsPerTick;
1676  if(prt) mf::LogVerbatim("TC")<<" doublet Chg "<<doubletChg<<" doubletTime "<<doubletTime<<" doubletRMSTimeErr "<<doubletRMSTimeErr;
1677  float doubletFOM = PointTrajDOCA(tjs, tp.Pos[0], doubletTime, tp) / doubletRMSTimeErr;
1678  if(doubletFOM < 0.5) doubletFOM = 0.5;
1679  float doubletChgPull = std::abs(doubletChg * pathInv / tp.AveChg - 1) / tj.ChgRMS;
1680  if(doubletChgPull > 1) doubletFOM *= chgWght * doubletChgPull;
1681  rmsRat = doubletWidthTick / expectedWidth;
1682  if(rmsRat < 1) rmsRat = 1 / rmsRat;
1683  doubletFOM *= rmsRat;
1684  if(prt) mf::LogVerbatim("TC")<<" doublet FOM "<<PointTrajDOCA(tjs, tp.Pos[0], doubletTime, tp)/doubletRMSTimeErr<<" doubletChgPull "<<doubletChgPull<<" rmsRat "<<rmsRat<<" doubletFOM "<<doubletFOM;
1685  if(doubletFOM < bestDeltaHitFOM && doubletFOM < otherHitFOM) {
1686  tp.UseHit[imbest] = true;
1687  tjs.fHits[bestDeltaHit].InTraj = tj.ID;
1688  tp.UseHit[ombest] = true;
1689  tjs.fHits[otherHit].InTraj = tj.ID;
1690  } else {
1691  // the doublet is not the best
1692  if(bestDeltaHitFOM < otherHitFOM) {
1693  tp.UseHit[imbest] = true;
1694  tjs.fHits[bestDeltaHit].InTraj = tj.ID;
1695  } else {
1696  tp.UseHit[ombest] = true;
1697  tjs.fHits[otherHit].InTraj = tj.ID;
1698  } // otherHit is the best
1699  } // doublet is not the best
1700  } else {
1701  // the other hit isn't available. Just use the singlet
1702  tp.UseHit[imbest] = true;
1703  tjs.fHits[bestDeltaHit].InTraj = tj.ID;
1704  }
1705  return;
1706  } // nAvailable == 2
1707 
1708  // we are left with nAvailable > 2
1709 
1710  // Use all of the hits if they are all available and are in the same multiplet
1711  if(nAvailable == tp.Hits.size()) {
1712  // the first hit in the multiplet
1713  unsigned int hit0 = tp.Hits[0] - tjs.fHits[tp.Hits[0]].LocalIndex;
1714  unsigned short cnt = 0;
1715  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1716  unsigned int iht = tp.Hits[ii];
1717  if(iht - tjs.fHits[iht].LocalIndex == hit0) ++cnt;
1718  } // ii
1719  // all in the same multiplet
1720  if(cnt == tp.Hits.size()) {
1721  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1722  unsigned int iht = tp.Hits[ii];
1723  if(tjs.fHits[iht].InTraj > 0) continue;
1724  tp.UseHit[ii] = true;
1725  tjs.fHits[iht].InTraj = tj.ID;
1726  } // ii
1727  return;
1728  } // all in the same multiplet
1729  } // nAvailable == tp.Hits.size()
1730 
1731  float hitsWidth = TPHitsRMSTick(tjs, tp, kUnusedHits);
1732  float maxTick = tp.Pos[1] / tjs.UnitsPerTick + 0.6 * expectedWidth;
1733  float minTick = tp.Pos[1] / tjs.UnitsPerTick - 0.6 * expectedWidth;
1734  if(prt) mf::LogVerbatim("TC")<<" Multiplet: hitsWidth "<<hitsWidth<<" expectedWidth "<<expectedWidth<<" tick range "<<(int)minTick<<" "<<(int)maxTick;
1735  // use all of the hits in the tick window
1736  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1737  unsigned int iht = tp.Hits[ii];
1738  if(tjs.fHits[iht].InTraj > 0) continue;
1739  if(tjs.fHits[iht].PeakTime < minTick) continue;
1740  if(tjs.fHits[iht].PeakTime > maxTick) continue;
1741  tp.UseHit[ii] = true;
1742  tjs.fHits[iht].InTraj = tj.ID;
1743  }
1744 
1745  } // FindUseHits
1746 
1749  {
1750  // Analyze the end of the Tj after crawling has stopped to see if any of the points
1751  // should be used
1752  // TODO: This function results in a small loss of efficiency and needs work. Perhaps by requir
1753 
1754  if(!tjs.UseAlg[kChkStopEP]) return;
1755  if(tj.AlgMod[kJunkTj]) return;
1756 
1757  unsigned short endPt = tj.EndPt[1];
1758  // ignore VLA Tjs
1759  if(tj.Pts[endPt].AngleCode > 1) return;
1760  // don't get too carried away with this
1761  if(tj.Pts.size() - endPt > 10) return;
1762 
1763  // Get a list of hits a few wires beyond the last point on the Tj
1764  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1765  unsigned short plane = planeID.Plane;
1766 
1767  // find the last point that has hits on it
1768  unsigned short lastPt = tj.Pts.size() - 1;
1769  for(lastPt = tj.Pts.size() - 1; lastPt >= tj.EndPt[1]; --lastPt) if(!tj.Pts[lastPt].Hits.empty()) break;
1770  auto& lastTP = tj.Pts[lastPt];
1771 
1772  if(prt) {
1773  mf::LogVerbatim("TC")<<"CSEP: checking "<<tj.ID<<" endPt "<<endPt<<" Pts size "<<tj.Pts.size()<<" lastPt Pos "<<PrintPos(tjs, lastTP.Pos);
1774  }
1775 
1776  // Check the charge and delta of the last point if there were many points fit
1777  if(lastTP.NTPsFit > 10 && lastTP.DeltaRMS > 0 && (lastTP.Delta / lastTP.DeltaRMS) > 3 && lastTP.ChgPull > 3) {
1778  if(prt) mf::LogVerbatim("TC")<<" Removing last TP with large Delta "<<lastTP.Delta<<" and large ChgPull "<<lastTP.ChgPull;
1779  UnsetUsedHits(tjs, lastTP);
1780  tj.AlgMod[kChkStopEP] = true;
1781  SetEndPoints(tjs, tj);
1782  // check again
1783  auto& tp = tj.Pts[tj.EndPt[1]];
1784  if(tp.DeltaRMS > 0 && (tp.Delta / tp.DeltaRMS) > 3 && tp.ChgPull > 3) {
1785  UnsetUsedHits(tjs, tp);
1786  SetEndPoints(tjs, tj);
1787  }
1788  return;
1789  }
1790 
1791  TrajPoint ltp;
1792  ltp.CTP = tj.CTP;
1793  ltp.Pos = tj.Pts[endPt].Pos;
1794  ltp.Dir = tj.Pts[endPt].Dir;
1795  double stepSize = std::abs(1/ltp.Dir[0]);
1796  std::array<int, 2> wireWindow;
1797  std::array<float, 2> timeWindow;
1798  std::vector<int> closeHits;
1799  bool isClean = true;
1800  for(unsigned short step = 0; step < 10; ++step) {
1801  for(unsigned short iwt = 0; iwt < 2; ++iwt) ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
1802  int wire = std::nearbyint(ltp.Pos[0]);
1803  wireWindow[0] = wire;
1804  wireWindow[1] = wire;
1805  timeWindow[0] = ltp.Pos[1] - 5;
1806  timeWindow[1] = ltp.Pos[1] + 5;
1807  bool hitsNear;
1808  auto tmp = FindCloseHits(tjs, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
1809  // add close hits that are not associated with this tj
1810  for(auto iht : tmp) if(tjs.fHits[iht].InTraj != tj.ID) closeHits.push_back(iht);
1811  float nWiresPast = 0;
1812  // Check beyond the end of the trajectory to see if there are hits there
1813  if(ltp.Dir[0] > 0) {
1814  // stepping +
1815  nWiresPast = ltp.Pos[0] - lastTP.Pos[0];
1816  } else {
1817  // stepping -
1818  nWiresPast = lastTP.Pos[0] - ltp.Pos[0];
1819  }
1820  if(prt) mf::LogVerbatim("TC")<<" Found "<<tmp.size()<<" hits near pos "<<PrintPos(tjs, ltp.Pos)<<" nWiresPast "<<nWiresPast;
1821  if(nWiresPast > 0.5) {
1822  if(!tmp.empty()) isClean = false;
1823  if(nWiresPast > 1.5) break;
1824  } // nWiresPast > 0.5
1825  } // step
1826 
1827  // count the number of available hits
1828  unsigned short nAvailable = 0;
1829  for(auto iht : closeHits) if(tjs.fHits[iht].InTraj == 0) ++nAvailable;
1830 
1831  if(prt) {
1832  mf::LogVerbatim myprt("TC");
1833  myprt<<"closeHits";
1834  for(auto iht : closeHits) myprt<<" "<<PrintHit(tjs.fHits[iht]);
1835  myprt<<" nAvailable "<<nAvailable;
1836  myprt<<" isClean "<<isClean;
1837  } // prt
1838 
1839  if(!isClean || nAvailable != closeHits.size()) return;
1840 
1841  unsigned short originalEndPt = tj.EndPt[1] + 1;
1842  // looks clean so use all the hits
1843  for(unsigned short ipt = originalEndPt; ipt <= lastPt; ++ipt) {
1844  auto& tp = tj.Pts[ipt];
1845  bool hitsAdded = false;
1846  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1847  // This shouldn't happen but check anyway
1848  if(tjs.fHits[tp.Hits[ii]].InTraj != 0) continue;
1849  tp.UseHit[ii] = true;
1850  tjs.fHits[tp.Hits[ii]].InTraj = tj.ID;
1851  hitsAdded = true;
1852  } // ii
1853  if(hitsAdded) DefineHitPos(tp);
1854  } // ipt
1855  tj.AlgMod[kChkStopEP] = true;
1856  SetEndPoints(tjs, tj);
1857  // Re-fitting the end might be a good idea but it's probably not necessary. The
1858  // values of Delta should have already been filled
1859 
1860  // require a Bragg peak
1861  ChkStop(tj);
1862  if(!tj.StopFlag[1][kBragg]) {
1863  // restore the original
1864  for(unsigned short ipt = originalEndPt; ipt <= lastPt; ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
1865  SetEndPoints(tjs, tj);
1866  } // no Bragg Peak
1867 
1868  UpdateTjChgProperties("CSEP", tjs, tj, prt);
1869 
1870  } // ChkStopEndPts
1871 
1874  {
1875  // defines HitPos, HitPosErr2 and Chg for the used hits in the trajectory point
1876 
1877  tp.Chg = 0;
1878  if(tp.Hits.empty()) return;
1879 
1880  unsigned short nused = 0;
1881  unsigned int iht = 0;
1882  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1883  if(tp.UseHit[ii]) {
1884  iht = tp.Hits[ii];
1885  ++nused;
1886  }
1887  }
1888  if(nused == 0) return;
1889 
1890  // don't bother with rest of this if there is only one hit since it can
1891  // only reside on one wire
1892  if(nused == 1) {
1893  tp.Chg = tjs.fHits[iht].Integral;
1894  // Normalize to 1 WSE path length
1895  float pathInv = std::abs(tp.Dir[0]);
1896  if(pathInv < 0.05) pathInv = 0.05;
1897  tp.Chg *= pathInv;
1898  tp.HitPos[0] = tjs.fHits[iht].ArtPtr->WireID().Wire;
1899  tp.HitPos[1] = tjs.fHits[iht].PeakTime * tjs.UnitsPerTick;
1900  float wireErr = tp.Dir[1] * 0.289;
1901  float timeErr = tp.Dir[0] * HitTimeErr(iht);
1902  tp.HitPosErr2 = wireErr * wireErr + timeErr * timeErr;
1903  if(prt) mf::LogVerbatim("TC")<<"DefineHitPos: singlet "<<std::fixed<<std::setprecision(1)<<tp.HitPos[0]<<":"<<(int)(tp.HitPos[1]/tjs.UnitsPerTick)<<" ticks. HitPosErr "<<sqrt(tp.HitPosErr2);
1904  return;
1905  } // nused == 1
1906 
1907  // multiple hits possibly on different wires
1908  std::vector<unsigned int> hitVec;
1909  tp.Chg = 0;
1910  std::array<float, 2> newpos;
1911  float chg;
1912  newpos[0] = 0;
1913  newpos[1] = 0;
1914  // Find the wire range for hits used in the TP
1915  unsigned int loWire = INT_MAX;
1916  unsigned int hiWire = 0;
1917  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1918  if(!tp.UseHit[ii]) continue;
1919  unsigned int iht = tp.Hits[ii];
1920  chg = tjs.fHits[iht].Integral;
1921  unsigned int wire = tjs.fHits[iht].ArtPtr->WireID().Wire;
1922  if(wire < loWire) loWire = wire;
1923  if(wire > hiWire) hiWire = wire;
1924  newpos[0] += chg * wire;
1925  newpos[1] += chg * tjs.fHits[iht].PeakTime;
1926  tp.Chg += chg;
1927  hitVec.push_back(iht);
1928  } // ii
1929 
1930  if(tp.Chg == 0) return;
1931 
1932  tp.HitPos[0] = newpos[0] / tp.Chg;
1933  tp.HitPos[1] = newpos[1] * tjs.UnitsPerTick / tp.Chg;
1934  // Normalize to 1 WSE path length
1935  float pathInv = std::abs(tp.Dir[0]);
1936  if(pathInv < 0.05) pathInv = 0.05;
1937  tp.Chg *= pathInv;
1938  // Error is the wire error (1/sqrt(12))^2 if all hits are on one wire.
1939  // Scale it by the wire range
1940  float dWire = 1 + hiWire - loWire;
1941  float wireErr = tp.Dir[1] * dWire * 0.289;
1942  float timeErr2 = tp.Dir[0] * tp.Dir[0] * HitsTimeErr2(hitVec);
1943  tp.HitPosErr2 = wireErr * wireErr + timeErr2;
1944  if(prt) mf::LogVerbatim("TC")<<"DefineHitPos: multiplet "<<std::fixed<<std::setprecision(1)<<tp.HitPos[0]<<":"<<(int)(tp.HitPos[1]/tjs.UnitsPerTick)<<" ticks. HitPosErr "<<sqrt(tp.HitPosErr2);
1945 
1946  } // HitPosErr2
1947 
1949  float TrajClusterAlg::HitTimeErr(const unsigned int iht)
1950  {
1951  return tjs.fHits[iht].RMS * tjs.UnitsPerTick * fHitErrFac * tjs.fHits[iht].Multiplicity;
1952  } // HitTimeErr
1953 
1955  float TrajClusterAlg::HitsTimeErr2(const std::vector<unsigned int>& hitVec)
1956  {
1957  // Estimates the error^2 of the time using all hits in hitVec
1958  if(hitVec.empty()) return 0;
1959  float err = fHitErrFac * HitsRMSTime(tjs, hitVec, kUnusedHits);
1960  return err * err;
1961  } // HitsTimeErr2
1962 
1964  void TrajClusterAlg::EndMerge(CTP_t inCTP, bool lastPass)
1965  {
1966  // Merges trajectories end-to-end or makes vertices. Does a more careful check on the last pass
1967 
1968  if(tjs.allTraj.size() < 2) return;
1969  if(!tjs.UseAlg[kMerge]) return;
1970 
1971  mrgPrt = (debug.Plane == (int)DecodeCTP(inCTP).Plane && debug.Wire < 0);
1972  if(mrgPrt) mf::LogVerbatim("TC")<<"inside EndMerge inCTP "<<inCTP<<" nTjs "<<tjs.allTraj.size()<<" lastPass? "<<lastPass;
1973 
1974  // Ensure that all tjs are in the same order
1975  for(auto& tj : tjs.allTraj) {
1976  if(tj.AlgMod[kKilled]) continue;
1977  if(tj.CTP != inCTP) continue;
1978  if(tj.StepDir != tjs.StepDir && !tj.AlgMod[kSetDir]) ReverseTraj(tjs, tj);
1979  } // tj
1980 
1981  unsigned short maxShortTjLen = tjs.Vertex2DCuts[0];
1982 
1983  // temp vector for checking the fraction of hits near a merge point
1984  std::vector<int> tjlist(2);
1985 
1986  float minChgRMS = 0.5 * (tjs.ChargeCuts[1] + tjs.ChargeCuts[2]);
1987 
1988  // iterate whenever a merge occurs since allTraj will change. This is not necessary
1989  // when a vertex is created however.
1990  bool iterate = true;
1991  while(iterate) {
1992  iterate = false;
1993  for(unsigned int it1 = 0; it1 < tjs.allTraj.size(); ++it1) {
1994  if(tjs.allTraj[it1].AlgMod[kKilled]) continue;
1995  if(tjs.allTraj[it1].CTP != inCTP) continue;
1996  auto& tj1 = tjs.allTraj[it1];
1997  for(unsigned short end1 = 0; end1 < 2; ++end1) {
1998  // no merge if there is a vertex at the end
1999  if(tj1.VtxID[end1] > 0) continue;
2000  // make a copy of tp1 so we can mess with it
2001  TrajPoint tp1 = tj1.Pts[tj1.EndPt[end1]];
2002  // do a local fit on the lastpass only using the last 3 points
2003  if(lastPass && tp1.NTPsFit > 3) {
2004  // make a local copy of the tj
2005  auto ttj = tjs.allTraj[it1];
2006  auto& lastTP = ttj.Pts[ttj.EndPt[end1]];
2007  // fit the last 3 points
2008  lastTP.NTPsFit = 3;
2009  FitTraj(tjs, ttj);
2010  tp1 = ttj.Pts[ttj.EndPt[end1]];
2011  } // last pass
2012  bool isVLA = (tp1.AngleCode == 2);
2013  float bestFOM = 5;
2014  if(isVLA) bestFOM = 20;
2015  float bestDOCA;
2016  unsigned int imbest = INT_MAX;
2017  for(unsigned int it2 = 0; it2 < tjs.allTraj.size(); ++it2) {
2018  if(it1 == it2) continue;
2019  auto& tj2 = tjs.allTraj[it2];
2020  // check for consistent direction
2021  if(tj1.StepDir != tj2.StepDir) continue;
2022  if(tj2.AlgMod[kKilled]) continue;
2023  if(tj2.CTP != inCTP) continue;
2024  // BB April 19, 2018: check for large fraction of overlapping wires
2025  float olf = OverlapFraction(tjs, tjs.allTraj[it1], tjs.allTraj[it2]);
2026 // if(mrgPrt) mf::LogVerbatim("TC")<<"EM: T"<<tjs.allTraj[it1].ID<<"-T"<<tjs.allTraj[it2].ID<<" OverlapFraction "<<olf;
2027  if(olf > 0.25) continue;
2028  unsigned short end2 = 1 - end1;
2029  // check for a vertex at this end
2030  if(tj2.VtxID[end2] > 0) continue;
2031  TrajPoint& tp2 = tj2.Pts[tj2.EndPt[end2]];
2032  TrajPoint& tp2OtherEnd = tj2.Pts[tj2.EndPt[end1]];
2033  // ensure that the other end isn't closer
2034  if(std::abs(tp2OtherEnd.Pos[0] - tp1.Pos[0]) < std::abs(tp2.Pos[0] - tp1.Pos[0])) continue;
2035  // ensure that the order is correct
2036  if(tjs.allTraj[it1].StepDir > 0) {
2037  if(tp2.Pos[0] < tp1.Pos[0] - 2) continue;
2038  } else {
2039  if(tp2.Pos[0] > tp1.Pos[0] + 2) continue;
2040  }
2041  // ensure that there is a signal on most of the wires between these points
2042  if(!SignalBetween(tjs, tp1, tp2, 0.8, mrgPrt)) {
2043 // if(mrgPrt) mf::LogVerbatim("TC")<<" no signal between "<<PrintPos(tjs, tp1.Pos)<<" and "<<PrintPos(tjs, tp2.Pos);
2044  continue;
2045  }
2046  // Find the distance of closest approach for small angle merging
2047  // Inflate the doca cut if we are bridging a block of dead wires
2048  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
2049  float doca = 15;
2050  if(isVLA) {
2051  // compare the minimum separation between Large Angle trajectories using a generous cut
2052  unsigned short ipt1, ipt2;
2053  TrajTrajDOCA(tjs, tjs.allTraj[it1], tjs.allTraj[it2], ipt1, ipt2, doca);
2054  if(mrgPrt) mf::LogVerbatim("TC")<<" isVLA check ipt1 "<<ipt1<<" ipt2 "<<ipt2<<" doca "<<doca;
2055  } else {
2056  // small angle
2057  doca = PointTrajDOCA(tjs, tp1.Pos[0], tp1.Pos[1], tp2);
2058  }
2059  float fom = dang * doca;
2060  if(fom < bestFOM) {
2061  bestFOM = fom;
2062  bestDOCA = doca;
2063  imbest = it2;
2064  }
2065  } // it2
2066  // No merge/vertex candidates
2067  if(imbest == INT_MAX) continue;
2068 
2069  // Make angle adjustments to tp1.
2070  unsigned int it2 = imbest;
2071  auto& tj2 = tjs.allTraj[imbest];
2072  unsigned short end2 = 1 - end1;
2073  bool loMCSMom = (tj1.MCSMom + tj2.MCSMom) < 150;
2074  // Don't use the angle at the end Pt for high momentum long trajectories in case there is a little kink at the end
2075  if(tj1.Pts.size() > 50 && tj1.MCSMom > 100) {
2076  if(end1 == 0) {
2077  tp1.Ang = tj1.Pts[tj1.EndPt[0] + 2].Ang;
2078  } else {
2079  tp1.Ang = tj1.Pts[tj1.EndPt[1] - 2].Ang;
2080  }
2081  } else if(loMCSMom) {
2082  // Low momentum - calculate the angle using the two Pts at the end
2083  unsigned short pt1, pt2;
2084  if(end1 == 0) {
2085  pt1 = tj1.EndPt[0];
2086  pt2 = pt1 + 1;
2087  } else {
2088  pt2 = tj1.EndPt[1];
2089  pt1 = pt2 - 1;
2090  }
2091  TrajPoint tpdir;
2092  if(MakeBareTrajPoint(tjs, tj1.Pts[pt1], tj1.Pts[pt2], tpdir)) tp1.Ang = tpdir.Ang;
2093  } // low MCSMom
2094  // Now do the same for tj2
2095  TrajPoint tp2 = tj2.Pts[tj2.EndPt[end2]];
2096  if(tj2.Pts.size() > 50 && tj2.MCSMom > 100) {
2097  if(end1 == 0) {
2098  tp2.Ang = tj2.Pts[tj2.EndPt[0] + 2].Ang;
2099  } else {
2100  tp2.Ang = tj2.Pts[tj2.EndPt[1] - 2].Ang;
2101  }
2102  } else if(loMCSMom) {
2103  // Low momentum - calculate the angle using the two Pts at the end
2104  unsigned short pt1, pt2;
2105  if(end2 == 0) {
2106  pt1 = tj2.EndPt[0];
2107  pt2 = pt1 + 1;
2108  } else {
2109  pt2 = tj2.EndPt[1];
2110  pt1 = pt2 - 1;
2111  }
2112  TrajPoint tpdir;
2113  if(MakeBareTrajPoint(tjs, tj2.Pts[pt1], tj2.Pts[pt2], tpdir)) tp2.Ang = tpdir.Ang;
2114  } // low MCSMom
2115 
2116  if(!isVLA && !SignalBetween(tjs, tp1, tp2, 0.99, mrgPrt)) continue;
2117 
2118  // decide whether to merge or make a vertex
2119  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
2120  float sep = PosSep(tp1.Pos, tp2.Pos);
2121 
2122  float dangCut;
2123  float docaCut;
2124  float chgPull = 0;
2125  if(tp1.AveChg > tp2.AveChg) {
2126  chgPull = (tp1.AveChg / tp2.AveChg - 1) / minChgRMS;
2127  } else {
2128  chgPull = (tp2.AveChg / tp1.AveChg - 1) / minChgRMS;
2129  }
2130  if(loMCSMom) {
2131  // increase dangCut dramatically for low MCSMom tjs
2132  dangCut = 1.0;
2133  // and the doca cut
2134  docaCut = 2;
2135  } else {
2136  // do a more careful calculation of the angle cut
2137  unsigned short e0 = tj1.EndPt[0];
2138  unsigned short e1 = tj1.EndPt[1];
2139  float tj1len = TrajPointSeparation(tj1.Pts[e0], tj1.Pts[e1]);
2140  float thetaRMS1 = MCSThetaRMS(tjs, tj1);
2141  // calculate (thetaRMS / sqrt(length) )^2
2142  thetaRMS1 *= thetaRMS1 / tj1len;
2143  // and now tj2
2144  e0 = tj2.EndPt[0];
2145  e1 = tj2.EndPt[1];
2146  float tj2len = TrajPointSeparation(tj2.Pts[e0], tj2.Pts[e1]);
2147  float thetaRMS2 = MCSThetaRMS(tjs, tj2);
2148  thetaRMS2 *= thetaRMS2 / tj2len;
2149  float dangErr = 0.5 * sqrt(thetaRMS1 + thetaRMS2);
2150  dangCut = tjs.KinkCuts[0] + tjs.KinkCuts[1] * dangErr;
2151  docaCut = 1;
2152  if(isVLA) docaCut = 15;
2153  }
2154 
2155  // open up the cuts on the last pass
2156  float chgFracCut = tjs.Vertex2DCuts[8];
2157  float chgPullCut = tjs.ChargeCuts[0];
2158  if(lastPass) {
2159  docaCut *= 2;
2160  chgFracCut *= 0.5;
2161  chgPullCut *= 1.5;
2162  }
2163 
2164  // check the merge cuts. Start with doca and dang requirements
2165  bool doMerge = bestDOCA < docaCut && dang < dangCut;
2166  bool showerTjs = tj1.PDGCode == 11 || tj2.PDGCode == 11;
2167  bool hiMCSMom = tj1.MCSMom > 200 || tj2.MCSMom > 200;
2168  // add a charge similarity requirement if not shower-like or low momentum or not LA
2169  if(doMerge && !showerTjs && hiMCSMom && chgPull > tjs.ChargeCuts[0] && !isVLA) doMerge = false;
2170  // ignore the charge pull cut if both are high momentum and dang is really small
2171  if(!doMerge && tj1.MCSMom > 900 && tj2.MCSMom > 900 && dang < 0.1 && bestDOCA < docaCut) doMerge = true;
2172 
2173  // do not merge if chgPull is really high
2174  if(doMerge && chgPull > 2 * chgPullCut) doMerge = false;
2175 
2176  if(doMerge) {
2177  if(lastPass) {
2178  // last pass cuts are looser but ensure that the tj after merging meets the quality cut
2179  float npwc = NumPtsWithCharge(tjs, tj1, true) + NumPtsWithCharge(tjs, tj2, true);
2180  auto& tp1OtherEnd = tj1.Pts[tj1.EndPt[1 - end1]];
2181  auto& tp2OtherEnd = tj2.Pts[tj2.EndPt[1 - end2]];
2182  float nwires = std::abs(tp1OtherEnd.Pos[0] - tp2OtherEnd.Pos[0]);
2183  if(nwires == 0) nwires = 1;
2184  float hitFrac = npwc / nwires;
2185  doMerge = (hitFrac > fQualityCuts[0]);
2186  if(prt) mf::LogVerbatim("TC")<<" lastPass merged Tj from "<<PrintPos(tjs, tp1OtherEnd.Pos)<<" to "<<PrintPos(tjs, tp2OtherEnd.Pos)<<" hitfrac "<<hitFrac<<" cut "<<fQualityCuts[0];
2187  } else {
2188  // don't merge if the gap between them is longer than the length of the shortest Tj
2189  float len1 = TrajLength(tjs.allTraj[it1]);
2190  float len2 = TrajLength(tjs.allTraj[it2]);
2191  if(len1 < len2) {
2192  if(sep > len1) doMerge = false;
2193  } else {
2194  if(sep > len2) doMerge = false;
2195  }
2196  if(prt) mf::LogVerbatim("TC")<<" merge check sep "<<sep<<" len1 "<<len1<<" len2 "<<len2<<" Merge? "<<doMerge;
2197  } // not lastPass
2198  } // doMerge
2199 
2200  // Require a large charge fraction near a merge point
2201  tjlist[0] = tjs.allTraj[it1].ID;
2202  tjlist[1] = tjs.allTraj[it2].ID;
2203  float chgFrac = ChgFracNearPos(tjs, tp1.Pos, tjlist);
2204  if(doMerge && bestDOCA > 1 && chgFrac < chgFracCut) doMerge = false;
2205 
2206  // don't merge if a Bragg peak exists. A vertex should be made instead
2207  if(doMerge && (tj1.StopFlag[end1][kBragg] || tj2.StopFlag[end2][kBragg])) doMerge = false;
2208 
2209  // Check the MCSMom asymmetry and don't merge if it is higher than the user-specified cut
2210  float momAsym = std::abs(tj1.MCSMom - tj2.MCSMom) / (float)(tj1.MCSMom + tj2.MCSMom);
2211  if(doMerge && momAsym > tjs.Vertex2DCuts[9]) doMerge = false;
2212 
2213  // don't allow vertices to be created between delta-rays
2214  // This needs to be done more carefully
2215 // if(!doMerge && (tj1.AlgMod[kDeltaRay] || tj2.AlgMod[kDeltaRay])) doMerge = true;
2216 
2217  if(mrgPrt) {
2218  mf::LogVerbatim myprt("TC");
2219  myprt<<"EM: T"<<tjs.allTraj[it1].ID<<"_"<<end1<<" - T"<<tjs.allTraj[it2].ID<<"_"<<end2<<" tp1-tp2 "<<PrintPos(tjs, tp1)<<"-"<<PrintPos(tjs, tp2);
2220  myprt<<" ShowerLike? "<<tjs.allTraj[it1].AlgMod[kShowerLike]<<" "<<tjs.allTraj[it2].AlgMod[kShowerLike];
2221  myprt<<" bestFOM "<<std::fixed<<std::setprecision(2)<<bestFOM;
2222  myprt<<" bestDOCA "<<std::setprecision(1)<<bestDOCA;
2223  myprt<<" cut "<<docaCut<<" isVLA? "<<isVLA;
2224  myprt<<" dang "<<std::setprecision(2)<<dang<<" dangCut "<<dangCut;
2225  myprt<<" chgPull "<<std::setprecision(1)<<chgPull<<" Cut "<<chgPullCut;
2226  myprt<<" chgFrac "<<std::setprecision(2)<<chgFrac;
2227  myprt<<" momAsym "<<momAsym;
2228  myprt<<" lastPass? "<<lastPass;
2229  myprt<<" doMerge? "<<doMerge;
2230  }
2231 
2232  if(bestDOCA > docaCut) continue;
2233 
2234  if(doMerge) {
2235  if(mrgPrt) mf::LogVerbatim("TC")<<" Merge ";
2236  bool didMerge = false;
2237  if(end1 == 1) {
2238  didMerge = MergeAndStore(tjs, it1, it2, mrgPrt);
2239  } else {
2240  didMerge = MergeAndStore(tjs, it2, it1, mrgPrt);
2241  }
2242  if(didMerge) {
2243  // Set the end merge flag for the killed trajectories to aid tracing merges
2244  tj1.AlgMod[kMerge] = true;
2245  tj1.AlgMod[kMerge] = true;
2246  iterate = true;
2247  } // Merge and store successfull
2248  else {
2249  if(mrgPrt) mf::LogVerbatim("TC")<<" MergeAndStore failed ";
2250  }
2251  } else {
2252  // create a vertex instead if it passes the vertex cuts
2253  VtxStore aVtx;
2254  aVtx.CTP = tjs.allTraj[it1].CTP;
2255  aVtx.ID = tjs.vtx.size() + 1;
2256  // keep it simple if tp1 and tp2 are very close or if the angle between them
2257  // is small
2258  if(PosSep(tp1.Pos, tp2.Pos) < 3 || dang < 0.1) {
2259  aVtx.Pos[0] = 0.5 * (tp1.Pos[0] + tp2.Pos[0]);
2260  aVtx.Pos[1] = 0.5 * (tp1.Pos[1] + tp2.Pos[1]);
2261  aVtx.Stat[kFixed] = true;
2262  } else {
2263  // Tps not so close
2264  // Dec 11, 2017. Require small separation in EndMerge.
2265 // float sepCut = tjs.Vertex2DCuts[2];
2266  float sepCut = tjs.Vertex2DCuts[1];
2267  bool tj1Short = (tjs.allTraj[it1].EndPt[1] - tjs.allTraj[it1].EndPt[0] < maxShortTjLen);
2268  bool tj2Short = (tjs.allTraj[it2].EndPt[1] - tjs.allTraj[it2].EndPt[0] < maxShortTjLen);
2269  if(tj1Short || tj2Short) sepCut = tjs.Vertex2DCuts[1];
2270  TrajIntersection(tp1, tp2, aVtx.Pos);
2271  float dw = aVtx.Pos[0] - tp1.Pos[0];
2272  if(std::abs(dw) > sepCut) continue;
2273  float dt = aVtx.Pos[1] - tp1.Pos[1];
2274  if(std::abs(dt) > sepCut) continue;
2275  dw = aVtx.Pos[0] - tp2.Pos[0];
2276  if(std::abs(dw) > sepCut) continue;
2277  dt = aVtx.Pos[1] - tp2.Pos[1];
2278  if(std::abs(dt) > sepCut) continue;
2279  // ensure that the vertex is not closer to the other end if the tj is short
2280  if(tj1Short) {
2281  TrajPoint otp1 = tjs.allTraj[it1].Pts[tjs.allTraj[it1].EndPt[1-end1]];
2282  if(PosSep2(otp1.Pos, aVtx.Pos) < PosSep2(tp1.Pos, aVtx.Pos)) continue;
2283  }
2284  if(tj2Short) {
2285  TrajPoint otp2 = tjs.allTraj[it2].Pts[tjs.allTraj[it2].EndPt[1-end2]];
2286  if(PosSep2(otp2.Pos, aVtx.Pos) < PosSep2(tp2.Pos, aVtx.Pos)) continue;
2287  }
2288  // we expect the vertex to be between tp1 and tp2
2289  if(aVtx.Pos[0] < tp1.Pos[0] && aVtx.Pos[0] < tp2.Pos[0]) {
2290  aVtx.Pos[0] = std::min(tp1.Pos[0], tp2.Pos[0]);
2291  aVtx.Stat[kFixed] = true;
2292  }
2293  if(aVtx.Pos[0] > tp1.Pos[0] && aVtx.Pos[0] > tp2.Pos[0]) {
2294  aVtx.Pos[0] = std::max(tp1.Pos[0], tp2.Pos[0]);
2295  aVtx.Stat[kFixed] = true;
2296  }
2297  } // Tps not so close
2298  // We got this far. Try a vertex fit to ensure that the errors are reasonable
2299  tjs.allTraj[it1].VtxID[end1] = aVtx.ID;
2300  tjs.allTraj[it2].VtxID[end2] = aVtx.ID;
2301  // save the position
2302  // do a fit
2303  if(!aVtx.Stat[kFixed] && !FitVertex(tjs, aVtx, mrgPrt)) {
2304  // back out
2305  tjs.allTraj[it1].VtxID[end1] = 0;
2306  tjs.allTraj[it2].VtxID[end2] = 0;
2307  if(mrgPrt) mf::LogVerbatim("TC")<<" Vertex fit failed ";
2308  continue;
2309  }
2310  aVtx.NTraj = 2;
2311  aVtx.Pass = tjs.allTraj[it1].Pass;
2312  aVtx.Topo = end1 + end2;
2313  tj1.AlgMod[kMerge] = true;
2314  tj2.AlgMod[kMerge] = true;
2315  // Set pion-like PDGCodes
2316  if(tj1.StopFlag[end1][kBragg] && !tj2.StopFlag[end2][kBragg]) tj1.PDGCode = 211;
2317  if(tj2.StopFlag[end2][kBragg] && !tj1.StopFlag[end1][kBragg]) tj2.PDGCode = 211;
2318  if(!StoreVertex(tjs, aVtx)) continue;
2319  SetVx2Score(tjs, prt);
2320  if(mrgPrt) {
2321  auto& newVx = tjs.vtx[tjs.vtx.size() - 1];
2322  mf::LogVerbatim("TC")<<" New vtx 2V"<<newVx.ID<<" at "<<(int)newVx.Pos[0]<<":"<<(int)(newVx.Pos[1]/tjs.UnitsPerTick)<<" Score "<<newVx.Score;
2323  }
2324  // check the score and kill it if it is below the cut
2325  auto& newVx2 = tjs.vtx[tjs.vtx.size() - 1];
2326  if(newVx2.Score < tjs.Vertex2DCuts[7] && CompatibleMerge(tjs, tj1, tj2, mrgPrt)) {
2327  tjs.allTraj[it1].VtxID[end1] = 0;
2328  tjs.allTraj[it2].VtxID[end2] = 0;
2329  tjs.vtx.pop_back();
2330  bool didMerge = false;
2331  if(end1 == 1) {
2332  didMerge = MergeAndStore(tjs, it1, it2, mrgPrt);
2333  } else {
2334  didMerge = MergeAndStore(tjs, it2, it1, mrgPrt);
2335  }
2336  if(didMerge) {
2337  // Set the end merge flag for the killed trajectories to aid tracing merges
2338  tj1.AlgMod[kMerge] = true;
2339  tj1.AlgMod[kMerge] = true;
2340  iterate = true;
2341  } // Merge and store successfull
2342  else {
2343  if(mrgPrt) mf::LogVerbatim("TC")<<" MergeAndStore failed ";
2344  }
2345  }
2346  } // create a vertex
2347  if(tj1.AlgMod[kKilled]) break;
2348  } // end1
2349  } // it1
2350  } // iterate
2351 
2352  ChkVxTjs(tjs, inCTP, mrgPrt);
2353 /*
2354  // Do some checking in debug mode
2355  if(tjs.DebugMode && lastPass) {
2356  for(unsigned short it1 = 0; it1 < tjs.allTraj.size() - 1; ++it1) {
2357  auto& tj1 = tjs.allTraj[it1];
2358  if(tj1.CTP != inCTP) continue;
2359  if(tj1.AlgMod[kKilled]) continue;
2360  for(unsigned short end1 = 0; end1 < 2; ++end1) {
2361  unsigned short end2 = 1 - end1;
2362  auto& tp1 = tj1.Pts[tj1.EndPt[end1]];
2363  for(unsigned short it2 = it1 + 1; it2 < tjs.allTraj.size(); ++it2) {
2364  auto& tj2 = tjs.allTraj[it2];
2365  if(tj2.CTP != inCTP) continue;
2366  if(tj2.AlgMod[kKilled]) continue;
2367  auto& tp2 = tj2.Pts[tj2.EndPt[end2]];
2368  float sep = PosSep2(tp1.HitPos, tp2.HitPos);
2369  if(sep < 2.5) {
2370  if(tj1.VtxID[end1] == 0 && tj2.VtxID[end2] == 0) {
2371  std::cout<<"Tjs "<<tj1.ID<<" and "<<tj2.ID<<" are close at Pos "<<tj1.CTP<<":"<<PrintPos(tjs, tp1.HitPos)<<" "<<tj2.CTP<<":"<<PrintPos(tjs, tp2.HitPos)<<" with no merge or vertex\n";
2372  } else if(tj1.VtxID[end1] != tj2.VtxID[end2]) {
2373  std::cout<<"Tjs "<<tj1.ID<<" and "<<tj2.ID<<" are close at Pos "<<tj1.CTP<<":"<<PrintPos(tjs, tp1.HitPos);
2374  std::cout<<" but have different vertex IDs "<<tj1.VtxID[end1]<<" != "<<tj2.VtxID[end2];
2375  std::cout<<"\n";
2376  }
2377  } // close points
2378  } // it2
2379  } // end1
2380  } // it1
2381  } // debug mode
2382 */
2383  } // EndMerge
2384 
2387  {
2388  // Crawl along the direction specified in the traj vector in steps of size step
2389  // (wire spacing equivalents). Find hits between the last trajectory point and
2390  // the last trajectory point + step. A new trajectory point is added if hits are
2391  // found. Crawling continues until no signal is found for two consecutive steps
2392  // or until a wire or time boundary is reached.
2393 
2394  fGoodTraj = false;
2395  fTryWithNextPass = false;
2396  if(tj.Pts.empty()) return;
2397 
2398  geo::PlaneID planeID = DecodeCTP(tj.CTP);
2399  if(planeID.Cryostat != tjs.TPCID.Cryostat || planeID.TPC != tjs.TPCID.TPC) return;
2400  unsigned short plane = planeID.Plane;
2401 
2402  unsigned short lastPtWithUsedHits = tj.EndPt[1];
2403 
2404  unsigned short lastPt = lastPtWithUsedHits;
2405  // Construct a local TP from the last TP that will be moved on each step.
2406  // Only the Pos and Dir variables will be used
2407  TrajPoint ltp;
2408  ltp.CTP = tj.CTP;
2409  ltp.Pos = tj.Pts[lastPt].Pos;
2410  ltp.Dir = tj.Pts[lastPt].Dir;
2411  // A second TP is cloned from the leading TP of tj, updated with hits, fit
2412  // parameters,etc and possibly pushed onto tj as the next TP
2413  TrajPoint tp ;
2414 
2415  // assume it is good from here on
2416  fGoodTraj = true;
2417 
2418  unsigned short nMissedSteps = 0;
2419 
2420  for(unsigned short step = 1; step < 10000; ++step) {
2421  // make a copy of the previous TP
2422  lastPt = tj.Pts.size() - 1;
2423  tp = tj.Pts[lastPt];
2424  ++tp.Step;
2425  double stepSize = fVLAStepSize;
2426  if(tp.AngleCode < 2) stepSize = std::abs(1/ltp.Dir[0]);
2427  // move the local TP position by one step in the right direction
2428  for(unsigned short iwt = 0; iwt < 2; ++iwt) ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
2429 
2430  unsigned short ivx = TPNearVertex(tjs, ltp);
2431  if(ivx != USHRT_MAX) {
2432  // Trajectory stops near a vertex so make the assignment
2433  AttachTrajToVertex(tjs, tj, tjs.vtx[ivx], prt);
2434  tj.StopFlag[1][kAtVtx] = true;
2435  break;
2436  }
2437 
2438  SetPDGCode(tjs, tj);
2439 
2440  // copy this position into tp
2441  tp.Pos = ltp.Pos;
2442  tp.Dir = ltp.Dir;
2443  if(prt) {
2444  mf::LogVerbatim("TC")<<"StepCrawl "<<step<<" Pos "<<tp.Pos[0]<<" "<<tp.Pos[1]<<" Dir "<<tp.Dir[0]<<" "<<tp.Dir[1]<<" stepSize "<<stepSize<<" AngCode "<<tp.AngleCode;
2445  }
2446  // hit the boundary of the TPC?
2447  if(tp.Pos[0] < 0 || tp.Pos[0] > tjs.MaxPos0[plane] ||
2448  tp.Pos[1] < 0 || tp.Pos[1] > tjs.MaxPos1[plane]) break;
2449  // remove the old hits and other stuff
2450  tp.Hits.clear();
2451  tp.UseHit.reset();
2452  tp.FitChi = 0; tp.Chg = 0;
2453  // append to the trajectory
2454  tj.Pts.push_back(tp);
2455  // update the index of the last TP
2456  lastPt = tj.Pts.size() - 1;
2457  // look for hits
2458  bool sigOK = false;
2459  AddHits(tj, lastPt, sigOK);
2460  // Check the stop flag
2461  if(tj.StopFlag[1][kAtTj]) break;
2462  // If successfull, AddHits has defined UseHit for this TP,
2463  // set the trajectory endpoints, and define HitPos.
2464  if(tj.Pts[lastPt].Hits.empty()) {
2465  // Require three points with charge on adjacent wires for small angle
2466  // stepping.
2467  if(tj.Pts[lastPt].AngleCode == 0 && lastPt == 2) return;
2468  // No close hits added.
2469  ++nMissedSteps;
2470  // First check for no signal in the vicinity
2471  if(lastPt > 0) {
2472  // break if this is a reverse propagate activity and there was no signal (not on a dead wire)
2473  if(!sigOK && tj.AlgMod[kRvPrp]) break;
2474  // Ensure that there is a signal here after missing a number of steps on a LA trajectory
2475  if(tj.Pts[lastPt].AngleCode > 0 && nMissedSteps > 4 && !SignalAtTp(tjs, ltp)) {
2476  tj.StopFlag[1][kSignal] = false;
2477  break;
2478  }
2479  // the last point with hits (used or not) is the previous point
2480  unsigned short lastPtWithHits = lastPt - 1;
2481  float tps = TrajPointSeparation(tj.Pts[lastPtWithHits], ltp);
2482  float dwc = DeadWireCount(tjs, ltp, tj.Pts[lastPtWithHits]);
2483  float nMissedWires = tps * std::abs(ltp.Dir[0]) - dwc;
2484  float maxWireSkip = fMaxWireSkipNoSignal;
2485  if(tj.PDGCode == 13) maxWireSkip = tjs.MuonTag[2];
2486  if(prt) mf::LogVerbatim("TC")<<" StepCrawl: no signal at ltp "<<PrintPos(tjs, ltp)<<" nMissedWires "<<std::fixed<<std::setprecision(1)<<nMissedWires<<" dead wire count "<<dwc<<" maxWireSkip "<<maxWireSkip<<" tj.PGDCode "<<tj.PDGCode;
2487  if(nMissedWires > maxWireSkip) {
2488  // We passed a number of wires without adding hits and are ready to quit.
2489  // First see if there is one good unused hit on the end TP and if so use it
2490  // lastPtWithHits + 1 == lastPt && tj.Pts[lastPtWithHits].Chg == 0 && tj.Pts[lastPtWithHits].Hits.size() == 1
2491  if(tj.EndPt[1] < tj.Pts.size() - 1 && tj.Pts[tj.EndPt[1]+1].Hits.size() == 1) {
2492  unsigned short lastLonelyPoint = tj.EndPt[1] + 1;
2493  unsigned int iht = tj.Pts[lastLonelyPoint].Hits[0];
2494  if(tjs.fHits[iht].InTraj == 0 && tj.Pts[lastLonelyPoint].Delta < 3 * tj.Pts[lastLonelyPoint].DeltaRMS) {
2495  tjs.fHits[iht].InTraj = tj.ID;
2496  tj.Pts[lastLonelyPoint].UseHit[0] = true;
2497  DefineHitPos(tj.Pts[lastLonelyPoint]);
2498  SetEndPoints(tjs, tj);
2499  if(prt) {
2500  mf::LogVerbatim("TC")<<" Added a Last Lonely Hit before breaking ";
2501  PrintTrajPoint("LLH", tjs, lastPt, tj.StepDir, tj.Pass, tj.Pts[lastLonelyPoint]);
2502  }
2503  }
2504  }
2505  break;
2506  }
2507  } // lastPt > 0
2508  // no sense keeping this TP on tj if no hits were added
2509  tj.Pts.pop_back();
2510  continue;
2511  } // tj.Pts[lastPt].Hits.empty()
2512  // ensure that we actually moved
2513  if(lastPt > 0 && PosSep2(tj.Pts[lastPt].Pos, tj.Pts[lastPt-1].Pos) < 0.1) return;
2514  // Found hits at this location so reset the missed steps counter
2515  nMissedSteps = 0;
2516  // Update the last point fit, etc using the just added hit(s)
2517  UpdateTraj(tj);
2518  // a failure occurred
2519  if(tj.NeedsUpdate) return;
2520  if(tj.Pts[lastPt].Chg == 0) {
2521  // There are points on the trajectory by none used in the last step. See
2522  // how long this has been going on
2523  float tps = TrajPointSeparation(tj.Pts[tj.EndPt[1]], ltp);
2524  float dwc = DeadWireCount(tjs, ltp, tj.Pts[tj.EndPt[1]]);
2525  float nMissedWires = tps * std::abs(ltp.Dir[0]) - dwc;
2526  if(prt) mf::LogVerbatim("TC")<<" Hits exist on the trajectory but are not used. Missed wires "<<std::nearbyint(nMissedWires)<<" dead wire count "<<(int)dwc;
2527  // break if this is a reverse propagate activity with no dead wires
2528  if(tj.AlgMod[kRvPrp] && dwc == 0) break;
2529  if(nMissedWires > fMaxWireSkipWithSignal) break;
2530  // try this out
2531  if(!MaskedHitsOK(tj)) {
2532  return;
2533  }
2534  // check for a series of bad fits and stop stepping
2535  if(tjs.UseAlg[kStopBadFits] && nMissedWires > 4 && StopIfBadFits(tj)) break;
2536  // Keep stepping
2537  if(prt) {
2538  if(tj.AlgMod[kRvPrp]) {
2539  PrintTrajectory("RP", tjs, tj, lastPt);
2540  } else {
2541  PrintTrajectory("SC", tjs, tj, lastPt);
2542  }
2543  }
2544  continue;
2545  } // tp.Hits.empty()
2546  if(tj.Pts.size() == 3) {
2547  // ensure that the last hit added is in the same direction as the first two.
2548  // This is a simple way of doing it
2549  bool badTj = (PosSep2(tj.Pts[0].HitPos, tj.Pts[2].HitPos) < PosSep2(tj.Pts[0].HitPos, tj.Pts[1].HitPos));
2550  // ensure that this didn't start as a small angle trajectory and immediately turn
2551  // into a large angle one
2552  if(!badTj && tj.Pts[lastPt].AngleCode > fMaxAngleCode[tj.Pass]) badTj = true;
2553  // check for a large change in angle
2554  if(!badTj) {
2555  float dang = DeltaAngle(tj.Pts[0].Ang, tj.Pts[2].Ang);
2556  if(dang > 0.5) badTj = false;
2557  }
2558  //check for a wacky delta
2559  if(!badTj && tj.Pts[2].Delta > 2) badTj = true;
2560  if(badTj) {
2561  if(prt) mf::LogVerbatim("TC")<<" Bad Tj found on the third point. Quit stepping.";
2562  fGoodTraj = false;
2563  return;
2564  }
2565  } // tj.Pts.size() == 3
2566  // Update the local TP with the updated position and direction
2567  ltp.Pos = tj.Pts[lastPt].Pos;
2568  ltp.Dir = tj.Pts[lastPt].Dir;
2569  if(fMaskedLastTP) {
2570  // see if TPs have been masked off many times and if the
2571  // environment is clean. If so, return and try with next pass
2572  // cuts
2573  if(!MaskedHitsOK(tj)) {
2574  if(prt) {
2575  if(tj.AlgMod[kRvPrp]) {
2576  PrintTrajectory("RP", tjs, tj, lastPt);
2577  } else {
2578  PrintTrajectory("SC", tjs, tj, lastPt);
2579  }
2580  }
2581  return;
2582  }
2583  // Don't bother with the rest of the checking below if we
2584  // set all hits not used on this TP
2585  if(prt) {
2586  if(tj.AlgMod[kRvPrp]) {
2587  PrintTrajectory("RP", tjs, tj, lastPt);
2588  } else {
2589  PrintTrajectory("SC", tjs, tj, lastPt);
2590  }
2591  }
2592  continue;
2593  }
2594  // We have added a TP with hits
2595  // assume that we aren't going to kill the point we just added, or any
2596  // of the previous points...
2597  unsigned short killPts = 0;
2598  // assume that we should keep going after killing points
2599  bool keepGoing = true;
2600  // check for a kink. Stop crawling if one is found
2601  GottaKink(tj, killPts);
2602  if(tj.StopFlag[1][kAtKink]) keepGoing = false;
2603  // See if the Chisq/DOF exceeds the maximum.
2604  // UpdateTraj should have reduced the number of points fit
2605  // as much as possible for this pass, so this trajectory is in trouble.
2606  if(killPts == 0 && tj.Pts[lastPt].FitChi > fMaxChi && tj.PDGCode != 13) {
2607  if(prt) mf::LogVerbatim("TC")<<" bad FitChi "<<tj.Pts[lastPt].FitChi<<" cut "<<fMaxChi;
2608  fGoodTraj = (NumPtsWithCharge(tjs, tj, true) > fMinPtsFit[tj.Pass]);
2609  return;
2610  }
2611  // print the local tp unless we have killing to do
2612  if(killPts == 0) {
2613  if(prt) {
2614  if(tj.AlgMod[kRvPrp]) {
2615  PrintTrajectory("RP", tjs, tj, lastPt);
2616  } else {
2617  PrintTrajectory("SC", tjs, tj, lastPt);
2618  }
2619  }
2620  } else {
2621  MaskTrajEndPoints(tj, killPts);
2622  if(!fGoodTraj) return;
2623  unsigned int onWire = (float)(std::nearbyint(tj.Pts[lastPt].Pos[0]));
2624  float nSteps = (float)(step - tj.Pts[lastPt - killPts].Step);
2625  if(prt) mf::LogVerbatim("TC")<<"TRP killing "<<killPts<<" after "<<nSteps<<" steps from prev TP. Current tp.Pos "<<tp.Pos[0]<<" "<<tp.Pos[1];
2626  // move the position
2627  tj.Pts[lastPt].Pos[0] += nSteps * tj.Pts[lastPt].Dir[0];
2628  tj.Pts[lastPt].Pos[1] += nSteps * tj.Pts[lastPt].Dir[1];
2629  if(tj.Pts[lastPt].AngleCode == 0) {
2630  // put the TP at the wire position prior to the move
2631  float dw = onWire - tj.Pts[lastPt].Pos[0];
2632  tj.Pts[lastPt].Pos[0] = onWire;
2633  tj.Pts[lastPt].Pos[1] += dw * tj.Pts[lastPt].Dir[1] / tj.Pts[lastPt].Dir[0];
2634  }
2635  // check the MCSMom after we going
2636  if(tj.Pts.size() > 20 && tj.Pass < fMinMCSMom.size() && tj.MCSMom < fMinMCSMom[tj.Pass]) break;
2637  // copy to the local trajectory point
2638  ltp.Pos = tj.Pts[lastPt].Pos;
2639  ltp.Dir = tj.Pts[lastPt].Dir;
2640  if(prt) mf::LogVerbatim("TC")<<" New ltp.Pos "<<ltp.Pos[0]<<" "<<ltp.Pos[1]<<" ticks "<<(int)ltp.Pos[1]/tjs.UnitsPerTick;
2641  if(!keepGoing) break;
2642  }
2643  } // step
2644 
2645  if(prt) mf::LogVerbatim("TC")<<"End StepCrawl with tj size "<<tj.Pts.size()<<" fGoodTraj = "<<fGoodTraj<<" with fTryWithNextPass "<<fTryWithNextPass;
2646 
2647  } // StepCrawl
2648 
2651  {
2652  // Sees if trajectory tj shares many hits with another trajectory and if so merges them.
2653 
2654  if(!tjs.UseAlg[kUseGhostHits]) return false;
2655  // ensure that tj is not a saved trajectory
2656  if(tj.ID > 0) return true;
2657  // or an already killed trajectory
2658  if(tj.AlgMod[kKilled]) return true;
2659  if(tj.Pts.size() < 3) return false;
2660 
2661  // vectors of traj IDs, and the occurrence count
2662  std::vector<int> tID;
2663  std::vector<unsigned short> tCnt;
2664 
2665  unsigned short hitCnt = 0;
2666  unsigned short nAvailable = 0;
2667  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2668  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2669  // ignore hits used by this trajectory
2670  if(tj.Pts[ipt].UseHit[ii]) {
2671  ++hitCnt;
2672  continue;
2673  }
2674  unsigned int iht = tj.Pts[ipt].Hits[ii];
2675  if(tjs.fHits[iht].InTraj > 0 && (unsigned int)tjs.fHits[iht].InTraj <= tjs.allTraj.size()) {
2676  int tjid = tjs.fHits[iht].InTraj;
2677  unsigned short indx;
2678  for(indx = 0; indx < tID.size(); ++indx) if(tID[indx] == tjid) break;
2679  if(indx == tID.size()) {
2680  tID.push_back(tjid);
2681  tCnt.push_back(1);
2682  } else {
2683  ++tCnt[indx];
2684  }
2685  } else {
2686  ++nAvailable;
2687  }
2688  } // ii
2689  } // ipt
2690 
2691  // Call it a ghost if > 1/3 of the hits are used by another trajectory
2692  hitCnt /= 3;
2693  int oldTjID = INT_MAX;
2694 
2695  if(prt) {
2696  mf::LogVerbatim myprt("TC");
2697  myprt<<"IsGhost tj hits size cut "<<hitCnt<<" tID_tCnt";
2698  for(unsigned short ii = 0; ii < tCnt.size(); ++ii) myprt<<" "<<tID[ii]<<"_"<<tCnt[ii];
2699  myprt<<"\nAvailable hits "<<nAvailable;
2700  } // prt
2701 
2702  for(unsigned short ii = 0; ii < tCnt.size(); ++ii) {
2703  if(tCnt[ii] > hitCnt) {
2704  oldTjID = tID[ii];
2705  hitCnt = tCnt[ii];
2706  }
2707  } // ii
2708  if(oldTjID == INT_MAX) return false;
2709  int oldTjIndex = oldTjID - 1;
2710 
2711  // See if this looks like a short delta-ray on a long muon
2712  Trajectory& oTj = tjs.allTraj[oldTjIndex];
2713  if(oTj.PDGCode == 13 && hitCnt < 0.1 * oTj.Pts.size()) return false;
2714 
2715  // See if there are gaps in this trajectory indicating that it is really a ghost and not
2716  // just a crossing trajectory
2717  // find the range of wires spanned by oTj
2718  int wire0 = INT_MAX;
2719  int wire1 = 0;
2720  for(auto& otp : oTj.Pts) {
2721  int wire = std::nearbyint(otp.Pos[0]);
2722  if(wire < wire0) wire0 = wire;
2723  if(wire > wire1) wire1 = wire;
2724  } // tp
2725 
2726  int nwires = wire1 - wire0 + 1;
2727  std::vector<float> oTjPos1(nwires, -1);
2728  unsigned short nMissedWires = 0;
2729  for(unsigned short ipt = oTj.EndPt[0]; ipt <= oTj.EndPt[1]; ++ipt) {
2730  if(oTj.Pts[ipt].Chg == 0) continue;
2731  int wire = std::nearbyint(oTj.Pts[ipt].Pos[0]);
2732  int indx = wire - wire0;
2733  if(indx < 0 || indx > nwires - 1) continue;
2734  oTjPos1[indx] = oTj.Pts[ipt].Pos[1];
2735  ++nMissedWires;
2736  } // ipt
2737  // count the number of ghost TPs
2738  unsigned short ngh = 0;
2739  // and the number with Delta > 0 relative to oTj
2740  unsigned short nghPlus = 0;
2741  // keep track of the first point and last point appearance of oTj
2742  unsigned short firstPtInoTj = USHRT_MAX;
2743  unsigned short lastPtInoTj = 0;
2744  TrajPoint tp = tj.Pts[tj.EndPt[0]];
2745  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2746  if(tj.Pts[ipt].Chg > 0) {
2747  tp = tj.Pts[ipt];
2748  continue;
2749  }
2750  int wire = std::nearbyint(tj.Pts[ipt].Pos[0]);
2751  int indx = wire - wire0;
2752  if(indx < 0 || indx > nwires - 1) continue;
2753  if(oTjPos1[indx] > 0) {
2754  // ensure that the hits in this tp are used in oTj
2755  bool HitInoTj = false;
2756  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2757  unsigned int iht = tj.Pts[ipt].Hits[ii];
2758  if(tjs.fHits[iht].InTraj == oldTjID) HitInoTj = true;
2759  } // ii
2760  if(HitInoTj) {
2761  ++ngh;
2762  MoveTPToWire(tp, tj.Pts[ipt].Pos[0]);
2763  if(tp.Pos[1] > oTjPos1[indx]) ++nghPlus;
2764  if(firstPtInoTj == USHRT_MAX) firstPtInoTj = ipt;
2765  lastPtInoTj = ipt;
2766  }
2767  } // oTjHasChg[indx]
2768  } // ipt
2769 
2770  if(prt) mf::LogVerbatim("TC")<<" Number of missed wires in oTj gaps "<<nMissedWires<<" Number of ghost hits in these gaps "<<ngh<<" nghPlus "<<nghPlus<<" cut "<<0.2 * nMissedWires;
2771 
2772  if(ngh < 0.2 * nMissedWires) return false;
2773  if(firstPtInoTj > lastPtInoTj) return false;
2774 
2775  // require all of the tj TPs to be on either the + or - side of the oTj trajectory
2776  if(!(nghPlus > 0.8 * ngh || nghPlus < 0.2 * ngh) ) return false;
2777 
2778  if(prt) mf::LogVerbatim("TC")<<" Trajectory is a ghost of "<<oldTjID<<" first point in oTj "<<firstPtInoTj<<" last point "<<lastPtInoTj;
2779 
2780  // unset all of the shared hits
2781  for(unsigned short ipt = firstPtInoTj; ipt <= lastPtInoTj; ++ipt) {
2782  if(tj.Pts[ipt].Chg == 0) continue;
2783  UnsetUsedHits(tjs, tj.Pts[ipt]);
2784  if(prt) PrintTrajectory("IG", tjs, tj, ipt);
2785  }
2786  // see how many points are left at the end
2787  ngh = 0;
2788  for(unsigned short ipt = lastPtInoTj; ipt <= tj.Pts.size(); ++ipt) {
2789  if(tj.Pts[ipt].Chg > 0) ++ngh;
2790  } // ipt
2791  // clobber those too?
2792  if(ngh > 0 && ngh < fMinPts[tj.Pass]) {
2793  for(unsigned short ipt = lastPtInoTj; ipt <= tj.EndPt[1]; ++ipt) {
2794  if(tj.Pts[ipt].Chg > 0) UnsetUsedHits(tjs, tj.Pts[ipt]);
2795  } // ipt
2796  }
2797  SetEndPoints(tjs, tj);
2798  tj.Pts.resize(tj.EndPt[1] + 1);
2799  tjs.allTraj[oldTjIndex].AlgMod[kUseGhostHits] = true;
2800  TrimEndPts("IG", tjs, tj, fQualityCuts, prt);
2801  if(tj.AlgMod[kKilled]) {
2802  fGoodTraj = false;
2803  if(prt) mf::LogVerbatim("TC")<<" Failed quality cuts";
2804  return true;
2805  }
2806  tj.MCSMom = MCSMom(tjs, tj);
2807  if(prt) mf::LogVerbatim("TC")<<" New tj size "<<tj.Pts.size();
2808  return true;
2809 
2810  } // IsGhost
2811 
2813  bool TrajClusterAlg::IsGhost(std::vector<unsigned int>& tHits, unsigned short& ofTraj)
2814  {
2815  // Called by FindJunkTraj to see if the passed hits are close to an existing
2816  // trajectory and if so, they will be used in that other trajectory
2817 
2818  ofTraj = USHRT_MAX;
2819 
2820  if(!tjs.UseAlg[kUseGhostHits]) return false;
2821 
2822  if(tHits.size() < 2) return false;
2823  // find all nearby hits
2824  std::vector<unsigned int> hitsInMuliplet, nearbyHits;
2825  for(auto iht : tHits) {
2826  GetHitMultiplet(iht, hitsInMuliplet);
2827  // prevent double counting
2828  for(auto mht : hitsInMuliplet) {
2829  if(std::find(nearbyHits.begin(), nearbyHits.end(), mht) == nearbyHits.end()) {
2830  nearbyHits.push_back(mht);
2831  }
2832  } // mht
2833  } // iht
2834 
2835  // vectors of traj IDs, and the occurrence count
2836  std::vector<unsigned short> tID, tCnt;
2837  unsigned short itj, indx;
2838  for(auto iht : nearbyHits) {
2839  if(tjs.fHits[iht].InTraj <= 0) continue;
2840  itj = tjs.fHits[iht].InTraj;
2841  for(indx = 0; indx < tID.size(); ++indx) if(tID[indx] == itj) break;
2842  if(indx == tID.size()) {
2843  tID.push_back(itj);
2844  tCnt.push_back(1);
2845  } else {
2846  ++tCnt[indx];
2847  }
2848  } // iht
2849  if(tCnt.empty()) return false;
2850 
2851  // Call it a ghost if > 50% of the hits are used by another trajectory
2852  unsigned short tCut = 0.5 * tHits.size();
2853  unsigned short ii, jj;
2854  itj = USHRT_MAX;
2855 
2856  if(prt) {
2857  mf::LogVerbatim myprt("TC");
2858  myprt<<"IsGhost tHits size "<<tHits.size()<<" cut fraction "<<tCut<<" tID_tCnt";
2859  for(ii = 0; ii < tCnt.size(); ++ii) myprt<<" "<<tID[ii]<<"_"<<tCnt[ii];
2860  } // prt
2861 
2862  for(ii = 0; ii < tCnt.size(); ++ii) {
2863  if(tCnt[ii] > tCut) {
2864  itj = tID[ii] - 1;
2865  break;
2866  }
2867  } // ii
2868  if(itj > tjs.allTraj.size() - 1) return false;
2869 
2870  if(prt) mf::LogVerbatim("TC")<<"is ghost of trajectory "<<tjs.allTraj[itj].ID;
2871 
2872  // Use all hits in tHits that are found in itj
2873  unsigned int iht, tht;
2874  for(auto& tp : tjs.allTraj[itj].Pts) {
2875  for(ii = 0; ii < tp.Hits.size(); ++ii) {
2876  iht = tp.Hits[ii];
2877  if(tjs.fHits[iht].InTraj != 0) continue;
2878  for(jj = 0; jj < tHits.size(); ++jj) {
2879  tht = tHits[jj];
2880  if(tht != iht) continue;
2881  tp.UseHit[ii] = true;
2882  tjs.fHits[iht].InTraj = tjs.allTraj[itj].ID;
2883  break;
2884  } // jj
2885  } // ii
2886  } // tp
2887  tjs.allTraj[itj].AlgMod[kUseGhostHits] = true;
2888  ofTraj = itj;
2889  return true;
2890 
2891  } // IsGhost
2892 
2895  {
2896  // Check the quality of the trajectory and possibly trim it or flag it for deletion
2897 
2898  if(!fGoodTraj) return;
2899 
2900  fTryWithNextPass = false;
2901 
2902  // ensure that the end points are defined
2903  SetEndPoints(tjs, tj);
2904  if(tj.EndPt[0] == tj.EndPt[1]) return;
2905 
2906  if(prt) {
2907  mf::LogVerbatim("TC")<<"inside CheckTraj with NumPtsWithCharge = "<<NumPtsWithCharge(tjs, tj, false);
2908  }
2909 
2910  if(NumPtsWithCharge(tjs, tj, false) < fMinPts[tj.Pass]) {
2911  fGoodTraj = false;
2912  return;
2913  }
2914 
2915  // Look for a charge asymmetry between points on both sides of a high-
2916  // charge point and trim points in the vicinity
2917  ChkChgAsymmetry(tjs, tj, prt);
2918 
2919  // flag this tj as a junk Tj (even though it wasn't created in FindJunkTraj).
2920  // Drop it and let FindJunkTraj do it's job
2921  TagJunkTj(tjs, tj, prt);
2922  if(tj.AlgMod[kJunkTj]) {
2923  fGoodTraj = false;
2924  return;
2925  }
2926 
2927  tj.MCSMom = MCSMom(tjs, tj);
2928 
2929  // See if the points at the stopping end can be included in the Tj
2930  ChkStopEndPts(tj, prt);
2931 
2932  // remove any points at the end that don't have charge
2933  tj.Pts.resize(tj.EndPt[1] + 1);
2934 
2935  // Ensure that a hit only appears once in the TJ
2936  if(HasDuplicateHits(tjs, tj, prt)) {
2937  if(prt) mf::LogVerbatim("TC")<<" HasDuplicateHits ";
2938  fGoodTraj = false;
2939  return;
2940  }
2941 
2942  // See if this is a ghost trajectory
2943  if(IsGhost(tj)) {
2944  if(prt) mf::LogVerbatim("TC")<<" CT: Ghost trajectory - trimmed hits ";
2945  if(!fGoodTraj) return;
2946  }
2947 
2948  if(tj.AlgMod[kJunkTj]) return;
2949 
2950  // checks are different for Very Large Angle trajectories
2951  bool isVLA = (tj.Pts[tj.EndPt[1]].AngleCode == 2);
2952  // The last two ranges are Large Angle and Very Large Angle. Determine if the TJ is Small Angle
2953  bool isSA = (tj.Pts[tj.EndPt[1]].AngleCode == 0);
2954 
2955  // First remove any TPs at the end that have no hits after
2956  // setting the StopFlag. Assume that there are no hits on TPs after the end
2957  tj.StopFlag[1][kSignal] = false;
2958  if(tj.EndPt[1] < tj.Pts.size() - 1) {
2959  // There must be hits at the end so set the kSignal StopFlag
2960  if(!tj.Pts[tj.EndPt[1]+1].Hits.empty()) tj.StopFlag[1][kSignal] = true;
2961  }
2962  tj.Pts.resize(tj.EndPt[1] + 1);
2963 
2964  // Fill in any gaps with hits that were skipped, most likely delta rays on muon tracks
2965  if(!isVLA) FillGaps(tj);
2966 
2967  if(prt) mf::LogVerbatim("TC")<<" CheckTraj MCSMom "<<tj.MCSMom<<" isVLA? "<<isVLA<<" NumPtsWithCharge "<<NumPtsWithCharge(tjs, tj, false)<<" Min Req'd "<<fMinPts[tj.Pass];
2968 
2969  // Check for hit width consistency on short trajectories
2970  if(tj.Pts.size() < 10) {
2971  float maxWidth = 0;
2972  float minWidth = 999;
2973  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2974  if(tj.Pts[ipt].Chg == 0) continue;
2975  if(tj.Pts[ipt].HitPosErr2 > maxWidth) maxWidth = tj.Pts[ipt].HitPosErr2;
2976  if(tj.Pts[ipt].HitPosErr2 < minWidth) minWidth = tj.Pts[ipt].HitPosErr2;
2977  } // ipt
2978  // Require less than a 3X difference in the hit width or 10X for HitPosErr2
2979  if(maxWidth > 10 * minWidth) {
2980  if(prt) mf::LogVerbatim("TC")<<" TP width variation too large: minWidth "<<minWidth<<" maxWidth "<<maxWidth;
2981  fGoodTraj = false;
2982  return;
2983  }
2984  } // short trajectory
2985 
2986  // Trim the end points until the TJ meets the quality cuts
2987  TrimEndPts("CT", tjs, tj, fQualityCuts, prt);
2988  if(tj.AlgMod[kKilled]) {
2989  fGoodTraj = false;
2990  return;
2991  }
2992 
2993  // Check for a Bragg peak at both ends. This may be used by FixTrajBegin.
2994  ChkStop(tj);
2995 
2996  // Update the trajectory parameters at the beginning of the trajectory
2997  FixTrajBegin(tj);
2998 
2999  // ignore short trajectories
3000  if(tj.EndPt[1] < 4) return;
3001 
3002  if(isSA && !tj.StopFlag[1][kBragg]) {
3003  // Small angle checks
3004 
3005  if(tjs.UseAlg[kCTKink] && tj.EndPt[1] > 8 && !tj.StopFlag[1][kAtKink] && tj.MCSMom > 50) {
3006  // look for the signature of a kink near the end of the trajectory.
3007  // These are: Increasing delta for the last few hits
3008  unsigned short newSize = USHRT_MAX;
3009  unsigned short lastPtToChk = tj.EndPt[1] - 4;
3010  float deltaCut = 2 * tj.Pts[lastPtToChk].DeltaRMS;
3011  for(unsigned short ipt = tj.EndPt[1]; ipt > lastPtToChk; --ipt) {
3012  // Stop checking if delta is good
3013  if(tj.Pts[ipt].Delta < deltaCut) break;
3014  float drat = tj.Pts[ipt].Delta / tj.Pts[ipt-1].Delta;
3015  if(drat > 1.2) newSize = ipt;
3016  } // ipt
3017  if(newSize != USHRT_MAX) {
3018  if(prt) mf::LogVerbatim("TC")<<"CTKink: Masking end points to newSize "<<newSize;
3019  for(unsigned short ipt = newSize; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
3020  SetEndPoints(tjs, tj);
3021  tj.AlgMod[kCTKink] = true;
3022  }
3023  } // tjs.UseAlg[kCTKink]
3024 
3025  if(tjs.UseAlg[kCTStepChk] && !tj.AlgMod[kRvPrp]) {
3026  // Compare the number of steps taken per TP near the beginning and
3027  // at the end. This will get confused if RevProp is used
3028  short nStepBegin = tj.Pts[2].Step - tj.Pts[1].Step;
3029  short nStepEnd;
3030  unsigned short lastPt = tj.Pts.size() - 1;
3031  unsigned short newSize = tj.Pts.size();
3032  for(unsigned short ipt = lastPt; ipt > lastPt - 2; --ipt) {
3033  nStepEnd = tj.Pts[ipt].Step - tj.Pts[ipt - 1].Step;
3034  if(nStepEnd > 3 * nStepBegin) newSize = ipt;
3035  }
3036  if(prt) mf::LogVerbatim("TC")<<"CTStepChk: check number of steps. newSize "<<newSize<<" tj.Pts.size() "<<tj.Pts.size();
3037  if(newSize < tj.Pts.size()) {
3038  for(unsigned short ipt = newSize; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
3039  SetEndPoints(tjs, tj);
3040  tj.AlgMod[kCTStepChk] = true;
3041  tj.Pts.resize(newSize);
3042  return;
3043  } // newSize < tj.Pts.size()
3044  } // tjs.UseAlg[kCTStepChk]
3045  } // isSA
3046 
3047  FindSoftKink(tj);
3048 
3049  HiEndDelta(tj);
3050 
3051  // final quality check
3052  float npwc = NumPtsWithCharge(tjs, tj, true);
3053  float npts = tj.EndPt[1] - tj.EndPt[0] + 1;
3054  float frac = npwc / npts;
3055  fGoodTraj = (frac >= fQualityCuts[0]);
3056  if(prt) mf::LogVerbatim("TC")<<"CTStepChk: fraction of points with charge "<<frac<<" good traj? "<<fGoodTraj;
3057  if(!fGoodTraj || fQuitAlg) return;
3058 
3059  // lop off high multiplicity hits at the end
3060  CheckHiMultEndHits(tj);
3061 
3062  // Check for a Bragg peak at both ends. This may be used by FixTrajBegin.
3063  ChkStop(tj);
3064 
3065  if(prt && tj.Pts.size() < 100) PrintTrajectory("CTo", tjs, tj, USHRT_MAX);
3066 
3067  } // CheckTraj
3068 
3071  {
3072  // Looks for a soft kink in the trajectory and truncates it if one is found.
3073  // This is best done after FixTrajBegin has been called.
3074 
3075  if(!tjs.UseAlg[kSoftKink]) return;
3076  if(tj.Pts.size() < 15) return;
3077  if(tj.MCSMom < 100) return;
3078 
3079  float dang = DeltaAngle(tj.Pts[tj.EndPt[0]].Ang, tj.Pts[tj.EndPt[1]].Ang);
3080 
3081  if(prt) {
3082  mf::LogVerbatim("TC")<<"FindSoftKink: "<<tj.ID<<" dang "<<dang<<" cut "<<0.5 * tjs.KinkCuts[0];
3083  }
3084  if(dang < 0.5 * tjs.KinkCuts[0]) return;
3085  // require at least 5 points fitted at the end of the trajectory
3086  unsigned short endPt = tj.EndPt[1];
3087  if(tj.Pts[endPt].NTPsFit < 5) return;
3088  if(tj.Pts[endPt].NTPsFit > endPt) return;
3089  // Estimate where where the kink would be
3090  unsigned short kinkPt = endPt - tj.Pts[endPt].NTPsFit;
3091  // Require at least 5 points in the trajectory before the kink
3092  if(prt) mf::LogVerbatim("TC")<<" kinkPt "<<kinkPt<<" NTPsFit at kinkPt "<<tj.Pts[kinkPt].NTPsFit<<" max "<<0.5 * kinkPt;
3093  if(kinkPt < 5) return;
3094  // require fewer points fitted in this region compared the number of points prior to it
3095  if(tj.Pts[kinkPt].NTPsFit > 0.5 * kinkPt) return;
3096  // scan back until we find the maximum number of points fitted
3097  unsigned short maxPtsFit = tj.Pts[kinkPt].NTPsFit;
3098  unsigned short atPt = kinkPt;
3099  for(unsigned short ipt = kinkPt; kinkPt > tj.EndPt[0] + 5; --ipt) {
3100  if(tj.Pts[ipt].NTPsFit > maxPtsFit) {
3101  maxPtsFit = tj.Pts[ipt].NTPsFit;
3102  atPt = ipt;
3103  }
3104  // stop scanning when the max starts falling
3105  if(tj.Pts[ipt].NTPsFit < maxPtsFit) break;
3106  if(ipt == 0) break;
3107  } // ipt
3108  if(atPt < 5) return;
3109  // require the trajectory be straight before the kink - the section we are going to keep
3110  if(MCSMom(tjs, tj, tj.EndPt[0], atPt) < 500) return;
3111  // release the hits in TPs after this point
3112  for(unsigned short ipt = atPt; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
3113  // Truncate the trajectory at this point
3114  tj.Pts.resize(atPt + 1);
3115  SetEndPoints(tjs, tj);
3116  tj.AlgMod[kSoftKink] = true;
3117  if(prt) mf::LogVerbatim("TC")<<" truncated trajectory at "<<PrintPos(tjs, tj.Pts[tj.Pts.size()-1]);
3118 
3119  } // FindSoftKinks
3120 
3123  {
3124  // Update the parameters at the beginning of the trajectory. The first
3125  // points may not belong to this trajectory since they were added when there was
3126  // little information. This information may be updated later if ReversePropagate is used
3127 
3128  if(!tjs.UseAlg[kFixBegin]) return;
3129  if(tj.AlgMod[kJunkTj]) return;
3130 
3131  // don't do anything if this tj has been modified by ReversePropagate
3132  if(tj.AlgMod[kRvPrp]) return;
3133 
3134  // don't bother with really short tjs
3135  if(tj.Pts.size() < 3) return;
3136 
3137  unsigned short lastPtToChk = 10;
3138  if(tjs.UseAlg[kFTBRvProp]) lastPtToChk = tj.EndPt[1];
3139 
3140  unsigned short atPt = tj.EndPt[1];
3141  unsigned short maxPtsFit = 0;
3142  for(unsigned short ipt = tj.EndPt[0]; ipt < lastPtToChk; ++ipt) {
3143  if(tj.Pts[ipt].Chg == 0) continue;
3144  if(tj.Pts[ipt].NTPsFit >= maxPtsFit) {
3145  maxPtsFit = tj.Pts[ipt].NTPsFit;
3146  atPt = ipt;
3147  // no reason to continue if there are a good number of points fitted
3148  if(maxPtsFit > 20) break;
3149  }
3150  } // ipt
3151  // find the first point that is in this fit
3152  unsigned short firstPtFit = tj.EndPt[0];
3153  unsigned short cnt = 0;
3154  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
3155  if(ii > atPt) break;
3156  unsigned short ipt = atPt - ii;
3157  if(tj.Pts[ipt].Chg == 0) continue;
3158  ++cnt;
3159  if(cnt == maxPtsFit) {
3160  firstPtFit = ipt;
3161  break;
3162  } // full count
3163  } // ii
3164 
3165  bool needsRevProp = firstPtFit > 3;
3166  if(prt) mf::LogVerbatim("TC")<<"FTB: firstPtFit "<<firstPtFit<<" atPt "<<atPt;
3167 
3168  if(!needsRevProp) {
3169  // check one wire on the other side of EndPt[0] to see if there are hits that are available which could
3170  // be picked up by reverse propagation
3171  TrajPoint tp = tj.Pts[0];
3172  tp.Hits.clear();
3173  tp.UseHit.reset();
3174  // Move the TP "backwards"
3175  double stepSize = fVLAStepSize;
3176  if(tp.AngleCode < 2) stepSize = std::abs(1/tp.Dir[0]);
3177  tp.Pos[0] -= tp.Dir[0] * stepSize * tj.StepDir;
3178  tp.Pos[1] -= tp.Dir[1] * stepSize * tj.StepDir;
3179  float maxDelta = 3 * tp.DeltaRMS;
3180  if(FindCloseHits(tjs, tp, maxDelta, kUnusedHits) && !tp.Hits.empty()) {
3181  needsRevProp = true;
3182  if(prt) {
3183  mf::LogVerbatim("TC")<<"FTB: Close unused hits found near EndPt[0] "<<tp.Hits.size()<<" or dead wire. Call ReversePropagate";
3184  PrintTrajPoint("FTB", tjs, 0, tj.StepDir, tj.Pass, tp);
3185  }
3186  }
3187  } // !needsRevProp
3188 
3189  if(prt) {
3190  mf::LogVerbatim("TC")<<"FTB: maxPtsFit "<<maxPtsFit<<" at point "<<atPt<<" firstPtFit "<<firstPtFit<<" Needs ReversePropagate? "<<needsRevProp;
3191  }
3192 
3193  if(tjs.UseAlg[kFTBRvProp] && needsRevProp) {
3194  // lop off the points before firstPtFit and reverse propagate
3195  if(prt) mf::LogVerbatim("TC")<<" clobber TPs "<<PrintPos(tjs, tj.Pts[0])<<" to "<<PrintPos(tjs, tj.Pts[atPt])<<". Call TrimEndPts then ReversePropagate ";
3196  for(unsigned short ipt = 0; ipt < firstPtFit; ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
3197  SetEndPoints(tjs, tj);
3198  tj.AlgMod[kFTBRvProp] = true;
3199  // Check for quality and trim if necessary before reverse propagation
3200  TrimEndPts("RPi", tjs, tj, fQualityCuts, prt);
3201  if(tj.AlgMod[kKilled]) {
3202  fGoodTraj = false;
3203  return;
3204  }
3205  ReversePropagate(tj);
3206  ChkStopEndPts(tj, prt);
3207  }
3208  // Clean up the first points if no reverse propagation was done
3209  if(!tj.AlgMod[kRvPrp]) FixTrajBegin(tj, atPt);
3210 
3211  } // FixTrajBegin
3212 
3214  void TrajClusterAlg::FixTrajBegin(Trajectory& tj, unsigned short atPt)
3215  {
3216  // Update the parameters at the beginning of the trajectory starting at point atPt
3217 
3218  if(!tjs.UseAlg[kFixBegin]) return;
3219  // ignore short trajectories
3220  unsigned short npwc = NumPtsWithCharge(tjs, tj, false);
3221  if(npwc < 6) return;
3222  // ignore somewhat longer trajectories that are curly
3223  if(npwc < 10 && tj.MCSMom < 100) return;
3224  // ignore shower-like trajectories
3225  if(tj.PDGCode == 11) return;
3226  // ignore junk trajectories
3227  if(tj.AlgMod[kJunkTj]) return;
3228  // ignore stopping trajectories
3229  if(tj.StopFlag[0][kBragg]) return;
3230 
3231 
3232  unsigned short firstPt = tj.EndPt[0];
3233  if(prt) {
3234  mf::LogVerbatim("TC")<<"FixTrajBegin: atPt "<<atPt<<" firstPt "<<firstPt<<" Stops at end 0? "<<PrintStopFlag(tj, 0);
3235  }
3236 
3237  if(atPt == tj.EndPt[0]) return;
3238 
3239  float maxDelta = 4 * tj.Pts[tj.EndPt[1]].DeltaRMS;
3240 
3241  // update the trajectory for all the points up to atPt
3242  // assume that we will use all of these points
3243  bool maskPts = false;
3244  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
3245  if(ii > atPt) break;
3246  unsigned int ipt = atPt - ii;
3247  TrajPoint& tp = tj.Pts[ipt];
3248  tp.Dir = tj.Pts[atPt].Dir;
3249  tp.Ang = tj.Pts[atPt].Ang;
3250  tp.AngErr = tj.Pts[atPt].AngErr;
3251  tp.AngleCode = tj.Pts[atPt].AngleCode;
3252  // Correct the projected time to the wire
3253  float dw = tp.Pos[0] - tj.Pts[atPt].Pos[0];
3254  if(tp.Dir[0] != 0) tp.Pos[1] = tj.Pts[atPt].Pos[1] + dw * tp.Dir[1] / tp.Dir[0];
3255  bool newHits = false;
3256  tj.Pts[ipt].Delta = PointTrajDOCA(tjs, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tj.Pts[ipt]);
3257  tj.Pts[ipt].DeltaRMS = tj.Pts[atPt].DeltaRMS;
3258  tj.Pts[ipt].NTPsFit = tj.Pts[atPt].NTPsFit;
3259  tj.Pts[ipt].FitChi = tj.Pts[atPt].FitChi;
3260  tj.Pts[ipt].AveChg = tj.Pts[atPt].AveChg;
3261  tj.Pts[ipt].ChgPull = (tj.Pts[ipt].Chg / tj.AveChg - 1) / tj.ChgRMS;
3262  if(tj.Pts[ipt].Delta > maxDelta) maskPts = true;
3263  if(maskPts) UnsetUsedHits(tjs, tp);
3264  if(prt) {
3265  if(newHits) {
3266  PrintTrajectory("FTB", tjs, tj, ipt);
3267  } else {
3268  PrintTrajectory("ftb", tjs, tj, ipt);
3269  }
3270  }
3271  if(ipt == 0) break;
3272  } // ii
3273  if(maskPts) SetEndPoints(tjs, tj);
3274  tj.AlgMod[kFixBegin] = true;
3275 
3276  } // FixTrajBegin
3277 
3278 
3280  void TrajClusterAlg::FixTrajEnd(Trajectory& tj, unsigned short atPt)
3281  {
3282  // Update the parameters at the end of the trajectory starting at point atPt
3283 
3284  if(!tjs.UseAlg[kFixEnd]) return;
3285  // ignore short trajectories
3286  unsigned short npwc = NumPtsWithCharge(tjs, tj, false);
3287  if(npwc < 6) return;
3288  // ignore somewhat longer trajectories that are curly
3289  if(npwc < 10 && tj.MCSMom < 100) return;
3290  // ignore shower-like trajectories
3291  if(tj.PDGCode == 11) return;
3292  // ignore junk trajectories
3293  if(tj.AlgMod[kJunkTj]) return;
3294  // ingore stopping trajectories
3295  if(tj.StopFlag[1][kBragg]) return;
3296 
3297  if(prt) {
3298  mf::LogVerbatim("TC")<<"FixTrajEnd: atPt "<<atPt;
3299  }
3300 
3301  if(atPt == tj.EndPt[1]) return;
3302 
3303  // update the trajectory for all the intervening points
3304  for(unsigned short ipt = atPt + 1; ipt <= tj.EndPt[1]; ++ipt) {
3305  TrajPoint& tp = tj.Pts[ipt];
3306  tp.Dir = tj.Pts[atPt].Dir;
3307  tp.Ang = tj.Pts[atPt].Ang;
3308  tp.AngErr = tj.Pts[atPt].AngErr;
3309  tp.AngleCode = tj.Pts[atPt].AngleCode;
3310  // Correct the projected time to the wire
3311  float dw = tp.Pos[0] - tj.Pts[atPt].Pos[0];
3312  if(tp.Dir[0] != 0) tp.Pos[1] = tj.Pts[atPt].Pos[1] + dw * tp.Dir[1] / tp.Dir[0];
3313  tj.Pts[ipt].Delta = PointTrajDOCA(tjs, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tj.Pts[ipt]);
3314  tj.Pts[ipt].DeltaRMS = tj.Pts[atPt].DeltaRMS;
3315  tj.Pts[ipt].NTPsFit = tj.Pts[atPt].NTPsFit;
3316  tj.Pts[ipt].FitChi = tj.Pts[atPt].FitChi;
3317  tj.Pts[ipt].AveChg = tj.Pts[atPt].AveChg;
3318  tj.Pts[ipt].ChgPull = (tj.Pts[ipt].Chg / tj.AveChg - 1) / tj.ChgRMS;
3319  if(prt) {
3320  PrintTrajectory("FTE", tjs, tj, ipt);
3321  }
3322  } // ipt
3323  tj.AlgMod[kFixEnd] = true;
3324 
3325  } // FixTrajEnd
3326 
3329  {
3330  // Fill in any gaps in the trajectory with close hits regardless of charge (well maybe not quite that)
3331 
3332  if(!tjs.UseAlg[kFillGap]) return;
3333  if(tj.AlgMod[kJunkTj]) return;
3334 
3335  if(prt) mf::LogVerbatim("TC")<<"FG: Check Tj "<<tj.ID<<" from "<<PrintPos(tjs, tj.Pts[tj.EndPt[0]])<<" to "<<PrintPos(tjs, tj.Pts[tj.EndPt[1]]);
3336 
3337  // start with the first point that has charge
3338  short firstPtWithChg = tj.EndPt[0];
3339  bool first = true;
3340  float maxDelta = 1;
3341  // don't let MCSMom suffer too much while filling gaps
3342  short minMCSMom = 0.7 * tj.MCSMom;
3343  while(firstPtWithChg < tj.EndPt[1]) {
3344  short nextPtWithChg = firstPtWithChg + 1;
3345  // find the next point with charge
3346  for(nextPtWithChg = firstPtWithChg + 1; nextPtWithChg < tj.EndPt[1]; ++nextPtWithChg) {
3347  if(tj.Pts[nextPtWithChg].Chg > 0) break;
3348  } // nextPtWithChg
3349  if(nextPtWithChg == firstPtWithChg + 1) {
3350  // the next point has charge
3351  ++firstPtWithChg;
3352  continue;
3353  }
3354  // Found a gap. Require at least two consecutive points with charge after the gap
3355  if(nextPtWithChg < (tj.EndPt[1] - 1) && tj.Pts[nextPtWithChg + 1].Chg == 0) {
3356  firstPtWithChg = nextPtWithChg;
3357  continue;
3358  }
3359  // Compare the charge before and after
3360  if(tj.Pts[firstPtWithChg].Chg > 0) {
3361  float chgrat = tj.Pts[nextPtWithChg].Chg / tj.Pts[firstPtWithChg].Chg;
3362  if(chgrat < 0.7 || chgrat > 1.4) {
3363  firstPtWithChg = nextPtWithChg;
3364  continue;
3365  }
3366  }
3367 
3368  // Make a bare trajectory point at firstPtWithChg that points to nextPtWithChg
3369  TrajPoint tp;
3370  if(!MakeBareTrajPoint(tjs, tj.Pts[firstPtWithChg], tj.Pts[nextPtWithChg], tp)) {
3371  fGoodTraj = false;
3372  return;
3373  }
3374  // Find the maximum delta between hits and the trajectory Pos for all
3375  // hits on this trajectory
3376  if(first) {
3377  maxDelta = 2.5 * MaxHitDelta(tjs, tj);
3378  first = false;
3379  } // first
3380  // define a loose charge cut using the average charge at the first point with charge
3381  float maxChg = tj.Pts[firstPtWithChg].AveChg * (1 + 2 * tjs.ChargeCuts[0] * tj.ChgRMS);
3382  // Eliminate the charge cut altogether if we are close to an end
3383  if(tj.Pts.size() < 10) {
3384  maxChg = 1E6;
3385  } else {
3386  short chgCutPt = tj.EndPt[0] + 5;
3387  if(firstPtWithChg < chgCutPt) {
3388  // gap is near end 0
3389  maxChg = 1E6;
3390  } else {
3391  // check for gap near end 1
3392  chgCutPt = tj.EndPt[1] - 5;
3393  if(chgCutPt < tj.EndPt[0]) chgCutPt = tj.EndPt[0];
3394  if(nextPtWithChg > chgCutPt) maxChg = 1E6;
3395  }
3396  }
3397 
3398  // fill in the gap
3399  for(unsigned short mpt = firstPtWithChg + 1; mpt < nextPtWithChg; ++mpt) {
3400  if(tj.Pts[mpt].Chg > 0) {
3401  mf::LogWarning("TC")<<"FillGaps coding error: firstPtWithChg "<<firstPtWithChg<<" mpt "<<mpt<<" nextPtWithChg "<<nextPtWithChg;
3402  fQuitAlg = true;
3403  return;
3404  }
3405  bool filled = false;
3406  float chg = 0;
3407  for(unsigned short ii = 0; ii < tj.Pts[mpt].Hits.size(); ++ii) {
3408  unsigned int iht = tj.Pts[mpt].Hits[ii];
3409  if(tjs.fHits[iht].InTraj > 0) continue;
3410  float delta = PointTrajDOCA(tjs, iht, tp);
3411  if(prt) mf::LogVerbatim("TC")<<" FG "<<PrintPos(tjs,tj.Pts[mpt])<<" hit "<<PrintHit(tjs.fHits[iht])<<" delta "<<delta<<" maxDelta "<<maxDelta<<" Chg "<<tjs.fHits[iht].Integral<<" maxChg "<<maxChg;
3412  if(delta > maxDelta) continue;
3413  tj.Pts[mpt].UseHit[ii] = true;
3414  tjs.fHits[iht].InTraj = tj.ID;
3415  chg += tjs.fHits[iht].Integral;
3416  filled = true;
3417  } // ii
3418  if(chg > maxChg || MCSMom(tjs, tj) < minMCSMom) {
3419  // don't use these hits after all
3420  UnsetUsedHits(tjs, tj.Pts[mpt]);
3421  filled = false;
3422  }
3423  if(filled) {
3424  DefineHitPos(tj.Pts[mpt]);
3425  tj.AlgMod[kFillGap] = true;
3426  if(prt) {
3427  PrintTrajPoint("FG", tjs, mpt, tj.StepDir, tj.Pass, tj.Pts[mpt]);
3428  mf::LogVerbatim("TC")<<"Check MCSMom "<<MCSMom(tjs, tj);
3429  }
3430  } // filled
3431  } // mpt
3432  firstPtWithChg = nextPtWithChg;
3433  } // firstPtWithChg
3434 
3435  if(tj.AlgMod[kFillGap]) tj.MCSMom = MCSMom(tjs, tj);
3436 
3437  } // FillGaps
3438 
3441  {
3442  // Modify the trajectory at the end if there is a consistent increase in delta. It
3443  // is called from CheckTraj.
3444  // This needs to be done carefully...
3445 
3446  if(!tjs.UseAlg[kHED]) return;
3447  if(tj.StopFlag[1][kBragg]) return;
3448  // Only consider long high momentum.
3449  if(tj.MCSMom < 100) return;
3450  if(tj.Pts.size() < 50) return;
3451 
3452  unsigned short ept = tj.EndPt[1];
3453 
3454  TrajPoint& lastTp = tj.Pts[ept];
3455 
3456  if(lastTp.AngleCode > 1) return;
3457  if(lastTp.FitChi < 1) return;
3458 
3459  unsigned short npts = USHRT_MAX;
3460  float lastDelta = lastTp.Delta;
3461  // check the last 20 points on the trajectory for a systematic increase in Delta and FitChi
3462  for(unsigned short ii = 1; ii < 20; ++ii) {
3463  unsigned short ipt = ept - ii;
3464  TrajPoint& tp = tj.Pts[ipt];
3465  if(tp.Chg == 0) continue;
3466  if(tp.FitChi < 1 || tp.Delta > lastDelta) {
3467  npts = ii;
3468  break;
3469  }
3470  lastDelta = tp.Delta;
3471  } // ii
3472 
3473  if(prt) mf::LogVerbatim("TC")<<"HED: last point FitChi "<<lastTp.FitChi<<" NTPsFit "<<lastTp.NTPsFit<<" new npts "<<npts;
3474 
3475  // something bad happened
3476  if(npts == USHRT_MAX) return;
3477  // The Tj end has some other problem
3478  if(npts < 4) return;
3479 
3480  // re-fit the end of the trajectory
3481  lastTp.NTPsFit = npts;
3482  FitTraj(tjs, tj);
3483  if(prt) PrintTrajPoint("HED", tjs, ept, tj.StepDir, tj.Pass, lastTp);
3484  // update the last points
3485  for(unsigned short ii = 1; ii <= npts; ++ii) {
3486  unsigned short ipt = ept - ii;
3487  TrajPoint& tp = tj.Pts[ipt];
3488  if(tp.Chg == 0) continue;
3489  tp.Dir = tj.Pts[ept].Dir;
3490  tp.Ang = tj.Pts[ept].Ang;
3491  tp.AngErr = tj.Pts[ept].AngErr;
3492  tp.AngleCode = tj.Pts[ept].AngleCode;
3493  // Correct the projected time to the wire
3494  float dw = tp.Pos[0] - tj.Pts[ept].Pos[0];
3495  if(tp.Dir[0] != 0) tp.Pos[1] = tj.Pts[ept].Pos[1] + dw * tp.Dir[1] / tp.Dir[0];
3496  tp.Delta = PointTrajDOCA(tjs, tp.HitPos[0], tp.HitPos[1], tp);
3497  tp.DeltaRMS = tj.Pts[ept].DeltaRMS;
3498  tp.NTPsFit = tj.Pts[ept].NTPsFit;
3499  tp.FitChi = tj.Pts[ept].FitChi;
3500  if(prt) PrintTrajPoint("HED", tjs, ipt, tj.StepDir, tj.Pass, tp);
3501  } // ii
3502 
3503  tj.AlgMod[kHED] = true;
3504 
3505  } // HiEndDelta
3506 
3509  {
3510  // Check for many unused hits in high multiplicity TPs in work and try to use them
3511 
3512  if(!tjs.UseAlg[kCHMUH]) return;
3513 
3514  // This code might do bad things to short trajectories
3515  if(NumPtsWithCharge(tjs, tj, true) < 6) return;
3516  if(tj.EndPt[0] == tj.EndPt[1]) return;
3517  // Angle code 0 tjs shouldn't have any high multiplicity hits added to them
3518  if(tj.Pts[tj.EndPt[1]].AngleCode == 0) return;
3519 
3520  // count the number of unused hits multiplicity > 1 hits and decide
3521  // if the unused hits should be used. This may trigger another
3522  // call to StepCrawl
3523  unsigned short ii, stopPt;
3524  // Use this to see if the high multiplicity Pts are mostly near
3525  // the end or further upstream
3526  unsigned short lastMult1Pt = USHRT_MAX;
3527  // the number of TPs with > 1 hit (HiMult)
3528  unsigned short nHiMultPt = 0;
3529  // the total number of hits associated with HiMult TPs
3530  unsigned short nHiMultPtHits = 0;
3531  // the total number of used hits associated with HiMult TPs
3532  unsigned short nHiMultPtUsedHits = 0;
3533  unsigned int iht;
3534  // start counting at the leading edge and break if a hit
3535  // is found that is used in a trajectory
3536  bool doBreak = false;
3537  unsigned short jj;
3538  for(ii = 1; ii < tj.Pts.size(); ++ii) {
3539  stopPt = tj.EndPt[1] - ii;
3540  for(jj = 0; jj < tj.Pts[stopPt].Hits.size(); ++jj) {
3541  iht = tj.Pts[stopPt].Hits[jj];
3542  if(tjs.fHits[iht].InTraj > 0) {
3543  doBreak = true;
3544  break;
3545  }
3546  } // jj
3547  if(doBreak) break;
3548  // require 2 consecutive multiplicity = 1 points
3549  if(lastMult1Pt == USHRT_MAX && tj.Pts[stopPt].Hits.size() == 1 && tj.Pts[stopPt-1].Hits.size() == 1) lastMult1Pt = stopPt;
3550  if(tj.Pts[stopPt].Hits.size() > 1) {
3551  ++nHiMultPt;
3552  nHiMultPtHits += tj.Pts[stopPt].Hits.size();
3553  nHiMultPtUsedHits += NumHitsInTP(tj.Pts[stopPt], kUsedHits);
3554  } // high multiplicity TP
3555  // stop looking when two consecutive single multiplicity TPs are found
3556  if(lastMult1Pt != USHRT_MAX) break;
3557  if(stopPt == 1) break;
3558  } // ii
3559  // Don't do this if there aren't a lot of high multiplicity TPs
3560  float fracHiMult = (float)nHiMultPt / (float)ii;
3561  if(lastMult1Pt != USHRT_MAX) {
3562  float nchk = tj.EndPt[1] - lastMult1Pt + 1;
3563  fracHiMult = (float)nHiMultPt / nchk;
3564  } else {
3565  fracHiMult = (float)nHiMultPt / (float)ii;
3566  }
3567  float fracHitsUsed = 0;
3568  if(nHiMultPt > 0 && nHiMultPtHits > 0) fracHitsUsed = (float)nHiMultPtUsedHits / (float)nHiMultPtHits;
3569  // Use this to limit the number of points fit for trajectories that
3570  // are close the LA tracking cut
3571  ii = tj.EndPt[1];
3572  bool sortaLargeAngle = (AngleRange(tjs, tj.Pts[ii]) == 1);
3573 
3574  if(prt) mf::LogVerbatim("TC")<<"CHMUH: First InTraj stopPt "<<stopPt<<" fracHiMult "<<fracHiMult<<" fracHitsUsed "<<fracHitsUsed<<" lastMult1Pt "<<lastMult1Pt<<" sortaLargeAngle "<<sortaLargeAngle;
3575  if(fracHiMult < 0.3) return;
3576  if(fracHitsUsed > 0.98) return;
3577 
3578  float maxDelta = 2.5 * MaxHitDelta(tjs, tj);
3579 
3580  if(prt) {
3581  mf::LogVerbatim("TC")<<" Pts size "<<tj.Pts.size()<<" nHiMultPt "<<nHiMultPt<<" nHiMultPtHits "<<nHiMultPtHits<<" nHiMultPtUsedHits "<<nHiMultPtUsedHits<<" sortaLargeAngle "<<sortaLargeAngle<<" maxHitDelta "<<maxDelta;
3582  }
3583 
3584  // Use next pass cuts if available
3585  if(sortaLargeAngle && tj.Pass < fMinPtsFit.size()-1) ++tj.Pass;
3586 
3587  // Make a copy of tj in case something bad happens
3588  Trajectory TjCopy = tj;
3589  // and the list of used hits
3590  auto inTrajHits = PutTrajHitsInVector(tj, kUsedHits);
3591  unsigned short ipt;
3592 
3593  // unset the used hits from stopPt + 1 to the end
3594  for(ipt = stopPt + 1; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(tjs, tj.Pts[ipt]);
3595  SetEndPoints(tjs, tj);
3596  unsigned short killPts;
3597  float delta;
3598  bool added;
3599  for(ipt = stopPt + 1; ipt < tj.Pts.size(); ++ipt) {
3600  // add hits that are within maxDelta and re-fit at each point
3601  added = false;
3602  for(ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3603  iht = tj.Pts[ipt].Hits[ii];
3604  if(prt) mf::LogVerbatim("TC")<<" ipt "<<ipt<<" hit "<<PrintHit(tjs.fHits[iht])<<" inTraj "<<tjs.fHits[iht].InTraj<<" delta "<<PointTrajDOCA(tjs, iht, tj.Pts[ipt]);
3605  if(tjs.fHits[iht].InTraj != 0) continue;
3606  delta = PointTrajDOCA(tjs, iht, tj.Pts[ipt]);
3607  if(delta > maxDelta) continue;
3608  if (!NumHitsInTP(TjCopy.Pts[ipt], kUsedHits)||TjCopy.Pts[ipt].UseHit[ii]){
3609  tj.Pts[ipt].UseHit[ii] = true;
3610  tjs.fHits[iht].InTraj = tj.ID;
3611  added = true;
3612  }
3613  } // ii
3614  if(added) DefineHitPos(tj.Pts[ipt]);
3615  if(tj.Pts[ipt].Chg == 0) continue;
3616  tj.EndPt[1] = ipt;
3617  // This will be incremented by one in UpdateTraj
3618  if(sortaLargeAngle) tj.Pts[ipt].NTPsFit = 2;
3619  UpdateTraj(tj);
3620  if(tj.NeedsUpdate) {
3621  if(prt) mf::LogVerbatim("TC")<<"UpdateTraj failed on point "<<ipt;
3622  // Clobber the used hits from the corrupted points in tj
3623  for(unsigned short jpt = stopPt + 1; jpt <= ipt; ++jpt) {
3624  for(unsigned short jj = 0; jj < tj.Pts[jpt].Hits.size(); ++jj) {
3625  if(tj.Pts[jpt].UseHit[jj]) tjs.fHits[tj.Pts[jpt].Hits[jj]].InTraj = 0;
3626  } // jj
3627  } // jpt
3628  // restore the original trajectory
3629  tj = TjCopy;
3630  // restore the hits
3631  for(unsigned short jpt = stopPt + 1; jpt <= ipt; ++jpt) {
3632  for(unsigned short jj = 0; jj < tj.Pts[jpt].Hits.size(); ++jj) {
3633  if(tj.Pts[jpt].UseHit[jj]) tjs.fHits[tj.Pts[jpt].Hits[jj]].InTraj = tj.ID;
3634  } // jj
3635  } // jpt
3636  return;
3637  }
3638  GottaKink(tj, killPts);
3639  if(killPts > 0) {
3640  MaskTrajEndPoints(tj, killPts);
3641  if(!fGoodTraj) return;
3642  break;
3643  }
3644  if(prt) PrintTrajectory("CHMUH", tjs, tj, ipt);
3645  } // ipt
3646  // if we made it here it must be OK
3647  SetEndPoints(tjs, tj);
3648  // Try to extend it, unless there was a kink
3649  if(tj.StopFlag[1][kAtKink]) return;
3650  // trim the end points although this shouldn't happen
3651  if(tj.EndPt[1] != tj.Pts.size() - 1) tj.Pts.resize(tj.EndPt[1] + 1);
3652  tj.AlgMod[kCHMUH] = true;
3653  } // CheckHiMultUnusedHits
3654 
3655 
3658  {
3659  // mask off high multiplicity TPs at the end
3660  if(!tjs.UseAlg[kCHMEH]) return;
3661  if(tj.StopFlag[1][kBragg]) return;
3662  if(tj.Pts.size() < 10) return;
3663  if(tj.Pts[tj.EndPt[1]].AngleCode == 0) return;
3664  // find the average multiplicity in the first half
3665  unsigned short aveMult= 0;
3666  unsigned short ipt, nhalf = tj.Pts.size() / 2;
3667  unsigned short cnt = 0;
3668  for(auto& tp : tj.Pts) {
3669  if(tp.Chg == 0) continue;
3670  aveMult += tp.Hits.size();
3671  ++cnt;
3672  if(cnt == nhalf) break;
3673  } // pt
3674  if(cnt == 0) return;
3675  aveMult /= cnt;
3676  if(aveMult == 0) aveMult = 1;
3677  // convert this into a cut
3678  aveMult *= 3;
3679  cnt = 0;
3680  for(ipt = tj.EndPt[1]; ipt > tj.EndPt[0]; --ipt) {
3681  if(tj.Pts[ipt].Chg == 0) continue;
3682  if(tj.Pts[ipt].Hits.size() > aveMult) {
3683  UnsetUsedHits(tjs, tj.Pts[ipt]);
3684  ++cnt;
3685  continue;
3686  }
3687  break;
3688  } // ipt
3689  if(prt) mf::LogVerbatim("TC")<<"CHMEH multiplicity cut "<<aveMult<<" number of TPs masked off "<<cnt;
3690  if(cnt > 0) {
3691  tj.AlgMod[kCHMEH] = true;
3692  SetEndPoints(tjs, tj);
3693  }
3694  } // CheckHiMultEndHits
3695 
3698  {
3699  // Returns true if there are a number of Tps that were not used in the trajectory because the fit was poor and the
3700  // charge pull is not really high. This
3701 
3702  // don't consider muons
3703  if(tj.PDGCode == 13) return false;
3704  // or long straight Tjs
3705  if(tj.Pts.size() > 40 && tj.MCSMom > 200) return false;
3706 
3707  unsigned short nBadFit = 0;
3708  unsigned short nHiChg = 0;
3709  unsigned short cnt = 0;
3710  for(unsigned short ipt = tj.Pts.size() - 1; ipt > tj.EndPt[1]; --ipt ) {
3711  if(tj.Pts[ipt].FitChi > 2) ++nBadFit;
3712  if(tj.Pts[ipt].ChgPull > 3) ++nHiChg;
3713  ++cnt;
3714  if(cnt == 5) break;
3715  } // ipt
3716 
3717  if(prt) mf::LogVerbatim("TC")<<"StopIfBadFits: nBadFit "<<nBadFit<<" nHiChg "<<nHiChg;
3718  if(nBadFit > 3 && nHiChg == 0) return true;
3719  return false;
3720 
3721  } // StopIfBadFits
3722 
3725  {
3726  // Version 2 of MaskedHitsOK.
3727  // The hits in the TP at the end of the trajectory were masked off. Decide whether to continue stepping with the
3728  // current configuration (true) or whether to stop and possibly try with the next pass settings (false)
3729 
3730  if(!tjs.UseAlg[kMaskHits]) return true;
3731 
3732  unsigned short lastPt = tj.Pts.size() - 1;
3733  if(tj.Pts[lastPt].Chg > 0) return true;
3734  unsigned short endPt = tj.EndPt[1];
3735 
3736  // count the number of points w/o used hits and the number with one unused hit
3737  unsigned short nMasked = 0;
3738  unsigned short nOneHit = 0;
3739  unsigned short nOKChg = 0;
3740  unsigned short nOKDelta = 0;
3741  // number of points with Pos > HitPos
3742  unsigned short nPosDelta = 0;
3743  // number of points with Delta increasing vs ipt
3744  unsigned short nDeltaIncreasing = 0;
3745  // Fake this a bit to simplify comparing the counts
3746  float prevDelta = tj.Pts[endPt].Delta;
3747  float maxOKDelta = 10 * tj.Pts[endPt].DeltaRMS;
3748  float maxOKChg = 0;
3749  // find the maximum charge point on the trajectory
3750  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) if(tj.Pts[ipt].Chg > maxOKChg) maxOKChg = tj.Pts[ipt].Chg;
3751  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
3752  unsigned short ipt = tj.Pts.size() - ii;
3753  auto& tp = tj.Pts[ipt];
3754  if(tp.Chg > 0) break;
3755  unsigned short nUnusedHits = 0;
3756  float chg = 0;
3757  for(unsigned short jj = 0; jj < tp.Hits.size(); ++jj) {
3758  unsigned int iht = tp.Hits[jj];
3759  if(tjs.fHits[iht].InTraj != 0) continue;
3760  ++nUnusedHits;
3761  chg += tjs.fHits[iht].Integral;
3762  } // jj
3763  if(chg < maxOKChg) ++nOKChg;
3764  if(nUnusedHits == 1) ++nOneHit;
3765  if(tp.Delta < maxOKDelta) ++nOKDelta;
3766  // count the number of points with Pos time > HitPos time
3767  if(tp.Pos[1] > tp.HitPos[1]) ++nPosDelta;
3768  // The number of increasing delta points: Note implied absolute value
3769  if(tp.Delta < prevDelta) ++nDeltaIncreasing;
3770  prevDelta = tp.Delta;
3771  ++nMasked;
3772  } // ii
3773 
3774  // determine if the hits are wandering away from the trajectory direction. This will result in
3775  // nPosDelta either being ~0 or ~equal to the number of masked points. nPosDelta should have something
3776  // in between these two extremes if we are stepping through a messy region
3777  bool driftingAway = nMasked > 2 && (nPosDelta == 0 || nPosDelta == nMasked);
3778  // Note that nDeltaIncreasing is always positive
3779  if(driftingAway && nDeltaIncreasing < nMasked - 1) driftingAway = false;
3780 
3781  if(prt) {
3782  mf::LogVerbatim("TC")<<"MHOK: nMasked "<<nMasked<<" nOneHit "<<nOneHit<<" nOKChg "<<nOKChg<<" nOKDelta "<<nOKDelta<<" nPosDelta "<<nPosDelta<<" nDeltaIncreasing "<<nDeltaIncreasing<<" driftingAway? "<<driftingAway;
3783  }
3784 
3785  if(!driftingAway) {
3786  if(nMasked < 8 || nOneHit < 8) return true;
3787  if(nOKDelta != nMasked) return true;
3788  if(nOKChg != nMasked) return true;
3789  }
3790 
3791  // we would like to reduce the number of fitted points to a minimum and include
3792  // the masked hits, but we can only do that if there are enough points
3793  if(tj.Pts[endPt].NTPsFit <= fMinPtsFit[tj.Pass]) {
3794  // stop stepping if we have masked off more points than are in the fit
3795  if(nMasked > tj.Pts[endPt].NTPsFit) return false;
3796  return true;
3797  }
3798  // Reduce the number of points fit and try to include the points
3799  unsigned short newNTPSFit;
3800  if(tj.Pts[endPt].NTPsFit > 2 * fMinPtsFit[tj.Pass]) {
3801  newNTPSFit = tj.Pts[endPt].NTPsFit / 2;
3802  } else {
3803  newNTPSFit = fMinPtsFit[tj.Pass];
3804  }
3805  for(unsigned ipt = endPt + 1; ipt < tj.Pts.size(); ++ipt) {
3806  TrajPoint& tp = tj.Pts[ipt];
3807  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3808  unsigned int iht = tp.Hits[ii];
3809  if(tjs.fHits[iht].InTraj == 0) {
3810  tp.UseHit[ii] = true;
3811  tjs.fHits[iht].InTraj = tj.ID;
3812  break;
3813  }
3814  } // ii
3815  DefineHitPos(tp);
3816  SetEndPoints(tjs, tj);
3817  tp.NTPsFit = newNTPSFit;
3818  FitTraj(tjs, tj);
3819  if(prt) PrintTrajectory("MHOK", tjs, tj, ipt);
3820  } // ipt
3821 
3822  tj.AlgMod[kMaskHits] = true;
3823  UpdateTjChgProperties("MHOK", tjs, tj, prt);
3824  return true;
3825 
3826  } // MaskedHitsOK
3827 
3830  {
3831  // Any re-sizing should have been done by the calling routine. This code updates the Pass and adjusts the number of
3832  // fitted points to get FitCHi < 2
3833 
3834  fTryWithNextPass = false;
3835 
3836  // See if there is another pass available
3837  if(tj.Pass > fMinPtsFit.size()-2) return;
3838  ++tj.Pass;
3839 
3840  unsigned short lastPt = tj.Pts.size() - 1;
3841  // Return if the last fit chisq is OK
3842  if(tj.Pts[lastPt].FitChi < 1.5) {
3843  fTryWithNextPass = true;
3844  return;
3845  }
3846  TrajPoint& lastTP = tj.Pts[lastPt];
3847  unsigned short newNTPSFit = lastTP.NTPsFit;
3848  // only give it a few tries before giving up
3849  unsigned short nit = 0;
3850 
3851  while(lastTP.FitChi > 1.5 && lastTP.NTPsFit > 2) {
3852  if(lastTP.NTPsFit > 3) newNTPSFit -= 2;
3853  else if(lastTP.NTPsFit == 3) newNTPSFit = 2;
3854  lastTP.NTPsFit = newNTPSFit;
3855  FitTraj(tjs, tj);
3856  if(prt) mf::LogVerbatim("TC")<<"PrepareForNextPass: FitChi is > 1.5 "<<lastTP.FitChi<<" Reduced NTPsFit to "<<lastTP.NTPsFit<<" tj.Pass "<<tj.Pass;
3857  if(lastTP.NTPsFit <= fMinPtsFit[tj.Pass]) break;
3858  ++nit;
3859  if(nit == 3) break;
3860  }
3861  // decide if the next pass should indeed be attempted
3862  if(lastTP.FitChi > 2) return;
3863  fTryWithNextPass = true;
3864 
3865  } // PrepareForNextPass
3866 
3868  void TrajClusterAlg::GottaKink(Trajectory& tj, unsigned short& killPts)
3869  {
3870  // Checks the last few points on the trajectory and returns with the number of
3871  // points (killPts) that should be killed (aka masked) at the end
3872  // tjs.KinkCuts
3873  // 0 = kink angle cut (radians)
3874  // 1 = kink angle significance cut
3875  // 2 = nPts fit at the end of the tj
3876  // Kink angle cut = tjs.KinkCuts[0] + tjs.KinkCuts[1] * MCSThetaRMS
3877 
3878  killPts = 0;
3879 
3880  // decide whether to turn kink checking back on
3881  if(tjs.KinkCuts[0] > 0 && tj.EndPt[1] == 20) {
3882  if(MCSMom(tjs, tj, 10, 19) > 50) tj.AlgMod[kNoKinkChk] = false;
3883  if(prt) mf::LogVerbatim("TC")<<"GottaKink turn kink checking back on? "<<tj.AlgMod[kNoKinkChk]<<" with MCSMom "<<MCSMom(tjs, tj, 10, 19);
3884  }
3885  if(tj.AlgMod[kNoKinkChk]) return;
3886 
3887  unsigned short lastPt = tj.EndPt[1];
3888  if(lastPt < 5) return;
3889  if(tj.Pts[lastPt].Chg == 0) return;
3890 
3891  // MCSThetaRMS is the scattering angle for the entire length of the trajectory. Convert
3892  // this to the scattering angle for one WSE unit
3893  float thetaRMS = MCSThetaRMS(tjs, tj, tj.EndPt[0], tj.EndPt[1]) / sqrt(TrajPointSeparation(tj.Pts[tj.EndPt[0]], tj.Pts[lastPt]));
3894  float kinkAngCut = tjs.KinkCuts[0] + tjs.KinkCuts[1] * thetaRMS;
3895  // relax this a bit when doing RevProp
3896  if(tj.AlgMod[kRvPrp]) kinkAngCut *= 1.3;
3897 
3898  // A simple check when there are few points being fit and the TJ is short.
3899  if(tj.Pts[lastPt].NTPsFit < 6 && tj.Pts.size() < 20) {
3900  unsigned short ii, prevPtWithHits = USHRT_MAX;
3901  unsigned short ipt;
3902  for(ii = 1; ii < tj.Pts.size(); ++ii) {
3903  ipt = lastPt - ii;
3904  if(tj.Pts[ipt].Chg > 0) {
3905  prevPtWithHits = ipt;
3906  break;
3907  }
3908  if(ipt == 0) break;
3909  } // ii
3910  if(prevPtWithHits == USHRT_MAX) return;
3911  float dang = DeltaAngle(tj.Pts[lastPt].Ang, tj.Pts[prevPtWithHits].Ang);
3912  kinkAngCut = 1.2 * tjs.KinkCuts[0];
3913  if(prt) mf::LogVerbatim("TC")<<"GottaKink Simple check lastPt "<<PrintPos(tjs,tj.Pts[lastPt])<<" dang "<<dang<<" cut "<<kinkAngCut;
3914  if(dang > kinkAngCut) {
3915  killPts = 1;
3916  tj.StopFlag[1][kAtKink] = true;
3917  }
3918  // Another case where there are few hits fit just prior to a dead wire
3919  // section or there were no hits added for several steps or due to a large
3920  // value of fMaxWireSkipNoSignal. We just added a bogus hit just after this section
3921  // so the trajectory angle change will be small. Find the angle between the previous
3922  // point fitted angle and the angle formed by the last two TPs
3923  if(std::abs(tj.Pts[lastPt-1].Pos[0] - tj.Pts[lastPt].Pos[0]) > 3) {
3924  TrajPoint tmp;
3925  if(!MakeBareTrajPoint(tjs, tj.Pts[lastPt-1], tj.Pts[lastPt], tmp)) {
3926  mf::LogVerbatim("TC")<<"GottaKink failure from MakeBareTrajPoint ";
3927  PrintTrajectory("GK", tjs, tj, USHRT_MAX);
3928  fGoodTraj = false;
3929  return;
3930  }
3931  dang = DeltaAngle(tmp.Ang, tj.Pts[prevPtWithHits].Ang);
3932  if(prt) mf::LogVerbatim("TC")<<"GottaKink Simple check after gap lastPt "<<lastPt<<" prevPtWithHits "<<prevPtWithHits<<" dang "<<dang<<" cut "<<kinkAngCut;
3933  if(dang > 1.5 * kinkAngCut) {
3934  killPts = 1;
3935  tj.StopFlag[1][kAtKink] = true;
3936  }
3937  }
3938  return;
3939  } // tj.Pts[lastPt].NTPsFit < 6 && tj.Pts.size() < 20
3940 
3941  if(tj.EndPt[1] < 10) return;
3942 
3943  unsigned short kinkPt = USHRT_MAX;
3944 
3945  // Find the kinkPt which is tjs.KinkCuts[2] from the end that has charge
3946  unsigned short cnt = 0;
3947  unsigned short nPtsFit = tjs.KinkCuts[2];
3948  unsigned short nHiMultPt = 0;
3949  unsigned short nHiChg = 0;
3950 
3951  for(unsigned short ii = 1; ii < lastPt; ++ii) {
3952  unsigned short ipt = lastPt - ii;
3953  if(tj.Pts[ipt].Chg == 0) continue;
3954  ++cnt;
3955  if(tj.Pts[ipt].Hits.size() > 1) ++nHiMultPt;
3956  if(tj.Pts[ipt].ChgPull > 1.5) ++nHiChg;
3957  if(cnt == nPtsFit) {
3958  kinkPt = ipt;
3959  break;
3960  }
3961  if(ipt == 0) break;
3962  } // ii
3963  if(kinkPt == USHRT_MAX) return;
3964 
3965  TrajPoint tpFit;
3966  unsigned short npts = 4;
3967  unsigned short fitDir = -1;
3968  FitTraj(tjs, tj, lastPt, npts, fitDir, tpFit);
3969  if(tpFit.FitChi > 1) return;
3970 
3971  float dang = DeltaAngle(tj.Pts[kinkPt].Ang, tpFit.Ang);
3972 
3973  if(dang > kinkAngCut) {
3974  killPts = nPtsFit;
3975  tj.StopFlag[1][kAtKink] = true;
3976  }
3977 
3978  if(killPts > 0) {
3979  // See if we are tracking a low momentum particle in which case we should just
3980  // turn off kink checking
3981  if(tjs.UseAlg[kNoKinkChk] && tj.EndPt[1] < 20) {
3982  // Find MCSMom if it hasn't been done
3983  if(tj.MCSMom < 0) tj.MCSMom = MCSMom(tjs, tj);
3984  if(tj.MCSMom < 50) {
3985  killPts = 0;
3986  tj.StopFlag[1][kAtKink] = false;
3987  tj.AlgMod[kNoKinkChk] = true;
3988  if(prt) mf::LogVerbatim("TC")<<"GottaKink turning off kink checking. MCSMom "<<tj.MCSMom;
3989  }
3990  } // turn off kink check
3991  // Don't stop if the last few points had high charge pull and we are tracking a muon, but do mask off the hits
3992  if(killPts > 0 && tj.PDGCode == 13 && tj.Pts[lastPt].ChgPull > 2 && tj.Pts[lastPt-1].ChgPull > 2) tj.StopFlag[1][kAtKink] = false;
3993  // Don't keep stepping or mask off any TPs if we hit a kink while doing RevProp
3994  if(tj.AlgMod[kRvPrp]) killPts = 0;
3995  // see if this is a stopping tj
3996  ChkStop(tj);
3997  if(tj.StopFlag[1][kBragg]) killPts = 0;
3998  // unset the stop bit
3999  tj.StopFlag[1][kBragg] = false;
4000  }
4001 
4002  if(prt) mf::LogVerbatim("TC")<<"GottaKink "<<kinkPt<<" Pos "<<PrintPos(tjs, tj.Pts[kinkPt])<<" dang "<<std::fixed<<std::setprecision(2)<<dang<<" cut "<<kinkAngCut<<" tpFit chi "<<tpFit.FitChi<<" killPts "<<killPts<<" GottaKink? "<<tj.StopFlag[1][kAtKink]<<" MCSMom "<<tj.MCSMom<<" thetaRMS "<<thetaRMS;
4003 
4004  } // GottaKink
4005 
4008  {
4009  // Updates the last added trajectory point fit, average hit rms, etc.
4010 
4011  tj.NeedsUpdate = true;
4012  fMaskedLastTP = false;
4013 
4014  if(tj.EndPt[1] < 1) return;
4015  unsigned int lastPt = tj.EndPt[1];
4016  TrajPoint& lastTP = tj.Pts[lastPt];
4017 
4018  // find the previous TP that has hits (and was therefore in the fit)
4019  unsigned short prevPtWithHits = USHRT_MAX;
4020  unsigned short firstFitPt = tj.EndPt[0];
4021  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
4022  unsigned short ipt = lastPt - ii;
4023  if(tj.Pts[ipt].Chg > 0) {
4024  prevPtWithHits = ipt;
4025  break;
4026  }
4027  if(ipt == 0) break;
4028  } // ii
4029  if(prevPtWithHits == USHRT_MAX) return;
4030 
4031  // define the FitChi threshold above which something will be done
4032  float maxChi = 2;
4033  unsigned short minPtsFit = fMinPtsFit[tj.Pass];
4034  // just starting out?
4035  if(lastPt < 4) minPtsFit = 2;
4036  // was !TrajIsClean...
4037  if(tj.PDGCode == 13 && TrajIsClean(tjs, tj, prt)) {
4038  // Fitting a clean muon
4039  maxChi = fMaxChi;
4040  minPtsFit = lastPt / 3;
4041  }
4042 
4043  // Set the lastPT delta before doing the fit
4044  lastTP.Delta = PointTrajDOCA(tjs, lastTP.HitPos[0], lastTP.HitPos[1], lastTP);
4045 
4046  // update MCSMom. First ensure that nothing bad has happened
4047  float newMCSMom = MCSMom(tjs, tj);
4048  if(lastPt > 10 && newMCSMom < 0.6 * tj.MCSMom) {
4049  if(prt) mf::LogVerbatim("TC")<<"UpdateTraj: MCSMom took a nose-dive "<<newMCSMom;
4050  UnsetUsedHits(tjs, lastTP);
4051  DefineHitPos(lastTP);
4052  SetEndPoints(tjs, tj);
4053  tj.NeedsUpdate = false;
4054  return;
4055  }
4056  tj.MCSMom = newMCSMom;
4057 
4058  if(prt) {
4059  mf::LogVerbatim("TC")<<"UpdateTraj: lastPt "<<lastPt<<" lastTP.Delta "<<lastTP.Delta<<" previous point with hits "<<prevPtWithHits<<" tj.Pts size "<<tj.Pts.size()<<" AngleCode "<<lastTP.AngleCode<<" PDGCode "<<tj.PDGCode<<" maxChi "<<maxChi<<" minPtsFit "<<minPtsFit<<" MCSMom "<<tj.MCSMom;
4060  }
4061 
4062  UpdateTjChgProperties("UT", tjs, tj, prt);
4063 
4064  if(lastPt == 1) {
4065  // Handle the second trajectory point. No error calculation. Just update
4066  // the position and direction
4067  lastTP.NTPsFit = 2;
4068  FitTraj(tjs, tj);
4069  lastTP.FitChi = 0.01;
4070  lastTP.AngErr = tj.Pts[0].AngErr;
4071  if(prt) mf::LogVerbatim("TC")<<"UpdateTraj: Second traj point pos "<<lastTP.Pos[0]<<" "<<lastTP.Pos[1]<<" dir "<<lastTP.Dir[0]<<" "<<lastTP.Dir[1];
4072  tj.NeedsUpdate = false;
4073  SetAngleCode(tjs, lastTP);
4074  return;
4075  }
4076 
4077  if(lastPt == 2) {
4078  // Third trajectory point. Keep it simple
4079  lastTP.NTPsFit = 3;
4080  FitTraj(tjs, tj);
4081  tj.NeedsUpdate = false;
4082  if(prt) mf::LogVerbatim("TC")<<"UpdateTraj: Third traj point fit "<<lastTP.FitChi;
4083  SetAngleCode(tjs, lastTP);
4084  return;
4085  }
4086 
4087  // Fit with > 2 TPs
4088  // Keep adding hits until Chi/DOF exceeds 1
4089  if(tj.Pts[prevPtWithHits].FitChi < 1) lastTP.NTPsFit += 1;
4090  // Reduce the number of points fit if the trajectory is long and chisq is getting a bit larger
4091  if(lastPt > 20 && tj.Pts[prevPtWithHits].FitChi > 1.5 && lastTP.NTPsFit > minPtsFit) lastTP.NTPsFit -= 2;
4092 
4093  FitTraj(tjs, tj);
4094 
4095  // don't get too fancy when we are starting out
4096  if(lastPt < 6) {
4097  tj.NeedsUpdate = false;
4098  UpdateDeltaRMS(tj);
4099  SetAngleCode(tjs, lastTP);
4100  if(prt) mf::LogVerbatim("TC")<<" Return with lastTP.FitChi "<<lastTP.FitChi<<" Chg "<<lastTP.Chg;
4101  return;
4102  }
4103 
4104  // find the first point that was fit.
4105  unsigned short cnt = 0;
4106  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
4107  unsigned short ipt = lastPt - ii;
4108  if(tj.Pts[ipt].Chg > 0) {
4109  firstFitPt = ipt;
4110  ++cnt;
4111  }
4112  if(cnt == lastTP.NTPsFit) break;
4113  if(ipt == 0) break;
4114  }
4115 
4116  unsigned short ndead = DeadWireCount(tjs, lastTP.HitPos[0], tj.Pts[firstFitPt].HitPos[0], tj.CTP);
4117 
4118  if(lastTP.FitChi > 1.5 && tj.Pts.size() > 6) {
4119  // A large chisq jump can occur if we just jumped a large block of dead wires. In
4120  // this case we don't want to mask off the last TP but reduce the number of fitted points
4121  // This count will be off if there a lot of dead or missing wires...
4122  // reduce the number of points significantly
4123  if(ndead > 5) {
4124  if(lastTP.NTPsFit > 5) lastTP.NTPsFit = 5;
4125  } else {
4126  // Have a longish trajectory and chisq was a bit large.
4127  // Was this a sudden occurrence and the fraction of TPs are included
4128  // in the fit? If so, we should mask off this
4129  // TP and keep going. If these conditions aren't met, we
4130  // should reduce the number of fitted points
4131  float chirat = 0;
4132  if(prevPtWithHits != USHRT_MAX && tj.Pts[prevPtWithHits].FitChi > 0) chirat = lastTP.FitChi / tj.Pts[prevPtWithHits].FitChi;
4133  // Don't mask hits when doing RevProp. Reduce NTPSFit instead
4134  fMaskedLastTP = (chirat > 1.5 && lastTP.NTPsFit > 0.3 * NumPtsWithCharge(tjs, tj, false) && !tj.AlgMod[kRvPrp]);
4135  // BB April 19, 2018: Don't mask TPs on low MCSMom Tjs
4136  if(fMaskedLastTP && tj.MCSMom < 30) fMaskedLastTP = false;
4137  if(prt) {
4138  mf::LogVerbatim("TC")<<" First fit chisq too large "<<lastTP.FitChi<<" prevPtWithHits chisq "<<tj.Pts[prevPtWithHits].FitChi<<" chirat "<<chirat<<" NumPtsWithCharge "<<NumPtsWithCharge(tjs, tj, false)<<" fMaskedLastTP "<<fMaskedLastTP;
4139  }
4140  // we should also mask off the last TP if there aren't enough hits
4141  // to satisfy the minPtsFit constraint
4142  if(!fMaskedLastTP && NumPtsWithCharge(tjs, tj, true) < minPtsFit) fMaskedLastTP = true;
4143  } // few dead wires
4144  } // lastTP.FitChi > 2 ...
4145 
4146  // Deal with a really long trajectory that is in trouble (uB cosmic).
4147  if(tj.PDGCode == 13 && lastTP.FitChi > fMaxChi) {
4148  if(lastTP.NTPsFit > 1.3 * tjs.MuonTag[0]) {
4149  lastTP.NTPsFit *= 0.8;
4150  if(prt) mf::LogVerbatim("TC")<<" Muon - Reduce NTPsFit "<<lastPt;
4151  } else {
4152  fMaskedLastTP = true;
4153  if(prt) mf::LogVerbatim("TC")<<" Muon - mask last point "<<lastPt;
4154  }
4155  }
4156 
4157  if(prt) mf::LogVerbatim("TC")<<"UpdateTraj: First fit "<<lastTP.Pos[0]<<" "<<lastTP.Pos[1]<<" dir "<<lastTP.Dir[0]<<" "<<lastTP.Dir[1]<<" FitChi "<<lastTP.FitChi<<" NTPsFit "<<lastTP.NTPsFit<<" ndead wires "<<ndead<<" fMaskedLastTP "<<fMaskedLastTP;
4158  if(fMaskedLastTP) {
4159  UnsetUsedHits(tjs, lastTP);
4160  DefineHitPos(lastTP);
4161  SetEndPoints(tjs, tj);
4162  lastPt = tj.EndPt[1];
4163  lastTP.NTPsFit -= 1;
4164  FitTraj(tjs, tj);
4165  UpdateTjChgProperties("UT", tjs, tj, prt);
4166  SetAngleCode(tjs, lastTP);
4167  return;
4168  } else {
4169  // a more gradual change in chisq. Maybe reduce the number of points
4170  unsigned short newNTPSFit = lastTP.NTPsFit;
4171  // reduce the number of points fit to keep Chisq/DOF < 2 adhering to the pass constraint
4172  // and also a minimum number of points fit requirement for long muons
4173  float prevChi = lastTP.FitChi;
4174  unsigned short ntry = 0;
4175  float chiCut = 1.5;
4176  while(lastTP.FitChi > chiCut && lastTP.NTPsFit > minPtsFit) {
4177  if(lastTP.NTPsFit > 15) {
4178  newNTPSFit = 0.7 * newNTPSFit;
4179  } else if(lastTP.NTPsFit > 4) {
4180  newNTPSFit -= 2;
4181  } else {
4182  newNTPSFit -= 1;
4183  }
4184  if(lastTP.NTPsFit < 3) newNTPSFit = 2;
4185  if(newNTPSFit < minPtsFit) newNTPSFit = minPtsFit;
4186  lastTP.NTPsFit = newNTPSFit;
4187  // BB April 19: try to add a last lonely hit on a low MCSMom tj on the last try
4188  if(newNTPSFit == minPtsFit && tj.MCSMom < 30) chiCut = 2;
4189  if(prt) mf::LogVerbatim("TC")<<" Bad FitChi "<<lastTP.FitChi<<" Reduced NTPsFit to "<<lastTP.NTPsFit<<" Pass "<<tj.Pass<<" chiCut "<<chiCut;
4190  FitTraj(tjs, tj);
4191  tj.NeedsUpdate = true;
4192  if(lastTP.FitChi > prevChi) {
4193  if(prt) mf::LogVerbatim("TC")<<" Chisq is increasing "<<lastTP.FitChi<<" Try to remove an earlier bad hit";
4194  MaskBadTPs(tj, chiCut);
4195  ++ntry;
4196  if(ntry == 2) break;
4197  }
4198  prevChi = lastTP.FitChi;
4199  if(lastTP.NTPsFit == minPtsFit) break;
4200  } // lastTP.FitChi > 2 && lastTP.NTPsFit > 2
4201  }
4202 
4203  // last ditch attempt if things look bad. Drop the last hit
4204  if(tj.Pts.size() > fMinPtsFit[tj.Pass] && lastTP.FitChi > maxChi) {
4205  if(prt) mf::LogVerbatim("TC")<<" Last try. Drop last TP "<<lastTP.FitChi<<" NTPsFit "<<lastTP.NTPsFit;
4206  UnsetUsedHits(tjs, lastTP);
4207  DefineHitPos(lastTP);
4208  SetEndPoints(tjs, tj);
4209  lastPt = tj.EndPt[1];
4210  FitTraj(tjs, tj);
4211  fMaskedLastTP = true;
4212  }
4213 
4214  if(tj.NeedsUpdate) UpdateTjChgProperties("UT", tjs, tj, prt);
4215 
4216  if(prt) mf::LogVerbatim("TC")<<" Fit done. Chi "<<lastTP.FitChi<<" NTPsFit "<<lastTP.NTPsFit;
4217 
4218  if(tj.EndPt[0] == tj.EndPt[1]) return;
4219 
4220  // Don't let the angle error get too small too soon. Stepping would stop if the first
4221  // few hits on a low momentum wandering track happen to have a very good fit to a straight line.
4222  // We will do this by averaging the default starting value of AngErr of the first TP with the current
4223  // value from FitTraj.
4224  if(lastPt < 14) {
4225  float defFrac = 1 / (float)(tj.EndPt[1]);
4226  lastTP.AngErr = defFrac * tj.Pts[0].AngErr + (1 - defFrac) * lastTP.AngErr;
4227  }
4228 
4229  UpdateDeltaRMS(tj);
4230  SetAngleCode(tjs, lastTP);
4231 
4232  tj.NeedsUpdate = false;
4233  return;
4234 
4235  } // UpdateTraj
4236 
4239  {
4240  // Estimate the Delta RMS of the TPs on the end of tj.
4241 
4242  unsigned int lastPt = tj.EndPt[1];
4243  TrajPoint& lastTP = tj.Pts[lastPt];
4244 
4245  if(lastTP.Chg == 0) return;
4246  if(lastPt < 6) return;
4247 
4248  unsigned short ii, ipt, cnt = 0;
4249  float sum = 0;
4250  for(ii = 1; ii < tj.Pts.size(); ++ii) {
4251  ipt = lastPt - ii;
4252  if(ipt > tj.Pts.size() - 1) break;
4253  if(tj.Pts[ipt].Chg == 0) continue;
4254  sum += PointTrajDOCA(tjs, tj.Pts[ipt].Pos[0], tj.Pts[ipt].Pos[1], lastTP);
4255  ++cnt;
4256  if(cnt == lastTP.NTPsFit) break;
4257  if(ipt == 0) break;
4258  }
4259  if(cnt < 3) return;
4260  // RMS of Gaussian distribution is ~1.2 x the average
4261  // of a one-sided Gaussian distribution (since Delta is > 0)
4262  lastTP.DeltaRMS = 1.2 * sum / (float)cnt;
4263  if(lastTP.DeltaRMS < 0.02) lastTP.DeltaRMS = 0.02;
4264 
4265  } // UpdateDeltaRMS
4266 
4268  bool TrajClusterAlg::StartTraj(Trajectory& tj, const unsigned int& fromHit, const unsigned int& toHit, const unsigned short& pass)
4269  {
4270  // Start a trajectory located at fromHit with direction pointing to toHit
4271  float fromWire = tjs.fHits[fromHit].ArtPtr->WireID().Wire;
4272  float fromTick = tjs.fHits[fromHit].PeakTime;
4273  float toWire = tjs.fHits[toHit].ArtPtr->WireID().Wire;
4274  float toTick = tjs.fHits[toHit].PeakTime;
4275  CTP_t tCTP = EncodeCTP(tjs.fHits[fromHit].ArtPtr->WireID());
4276  return StartTraj(tj, fromWire, fromTick, toWire, toTick, tCTP, pass);
4277  } // StartTraj
4278 
4280  bool TrajClusterAlg::StartTraj(Trajectory& tj, const float& fromWire, const float& fromTick, const float& toWire, const float& toTick, const CTP_t& tCTP, const unsigned short& pass)
4281  {
4282  // Start a simple (seed) trajectory going from (fromWire, toTick) to (toWire, toTick).
4283 
4284  // decrement the work ID so we can use it for debugging problems
4285  --fWorkID;
4286  if(fWorkID == INT_MIN) fWorkID = -1;
4287  tj.ID = fWorkID;
4288  tj.Pass = pass;
4289  // Assume we are stepping in the positive WSE units direction
4290  short stepdir = 1;
4291  int fWire = std::nearbyint(fromWire);
4292  int tWire = std::nearbyint(toWire);
4293  if(tWire < fWire) {
4294  stepdir = -1;
4295  } else if(tWire == fWire) {
4296  // on the same wire
4297  if(toTick < fromTick) stepdir = -1;
4298  }
4299  tj.StepDir = stepdir;
4300  tj.CTP = tCTP;
4301  tj.ParentID = -1;
4302 
4303  // create a trajectory point
4304  TrajPoint tp;
4305  if(!MakeBareTrajPoint(tjs, fromWire, fromTick, toWire, toTick, tCTP, tp)) {
4306  mf::LogVerbatim("TC")<<"StartTraj: Failure from MakeBareTrajPoint fromWire "<<fromWire<<" fromTick "<<fromTick<<" toWire "<<toWire<<" toTick "<<toTick;
4307  return false;
4308  }
4309  SetAngleCode(tjs, tp);
4310  tp.AngErr = 0.1;
4311 // prt = false;
4312  if(tj.ID == debug.WorkID) { prt = true; didPrt = true; debug.Plane = DecodeCTP(tCTP).Plane; TJPrt = tj.ID; }
4313  if(prt) {
4314  mf::LogVerbatim("TC")<<"StartTraj "<<(int)fromWire<<":"<<(int)fromTick<<" -> "<<(int)toWire<<":"<<(int)toTick<<" StepDir "<<tj.StepDir<<" dir "<<tp.Dir[0]<<" "<<tp.Dir[1]<<" ang "<<tp.Ang<<" AngleCode "<<tp.AngleCode<<" angErr "<<tp.AngErr<<" ExpectedHitsRMS "<<ExpectedHitsRMS(tjs, tp);
4315  }
4316  tj.Pts.push_back(tp);
4317  return true;
4318 
4319  } // StartTraj
4320 
4322  void TrajClusterAlg::ChkInTraj(std::string someText)
4323  {
4324  // Check tjs.allTraj -> InTraj associations
4325 
4326  if(!tjs.UseAlg[kChkInTraj]) return;
4327 
4329 
4330  int tID;
4331  unsigned int iht;
4332  unsigned short itj = 0;
4333  std::vector<unsigned int> tHits;
4334  std::vector<unsigned int> atHits;
4335  for(auto& tj : tjs.allTraj) {
4336  // ignore abandoned trajectories
4337  if(tj.AlgMod[kKilled]) continue;
4338  tID = tj.ID;
4339  for(auto& tp : tj.Pts) {
4340  if(tp.Hits.size() > 16) {
4341  tj.AlgMod[kKilled] = true;
4342  mf::LogWarning("TC")<<"ChkInTraj: More than 16 hits created a UseHit bitset overflow\n";
4343  fQuitAlg = true;
4344  return;
4345  }
4346  } // tp
4347  if(tj.AlgMod[kKilled]) {
4348  std::cout<<someText<<" ChkInTraj hit size mis-match in tj ID "<<tj.ID<<" AlgBitNames";
4349  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) std::cout<<" "<<AlgBitNames[ib];
4350  std::cout<<"\n";
4351  continue;
4352  }
4353  tHits = PutTrajHitsInVector(tj, kUsedHits);
4354  if(tHits.size() < 2) {
4355  mf::LogVerbatim("TC")<<someText<<" ChkInTraj: Insufficient hits in traj "<<tj.ID<<" it";
4356  PrintTrajectory("CIT", tjs, tj, USHRT_MAX);
4357  tj.AlgMod[kKilled] = true;
4358  continue;
4359  }
4360  std::sort(tHits.begin(), tHits.end());
4361  atHits.clear();
4362  for(iht = 0; iht < tjs.fHits.size(); ++iht) {
4363  if(tjs.fHits[iht].InTraj == tID) atHits.push_back(iht);
4364  } // iht
4365  if(atHits.size() < 2) {
4366  mf::LogVerbatim("TC")<<someText<<" ChkInTraj: Insufficient hits in atHits in traj "<<tj.ID<<" Killing it";
4367  tj.AlgMod[kKilled] = true;
4368  continue;
4369  }
4370  if(!std::equal(tHits.begin(), tHits.end(), atHits.begin())) {
4371  mf::LogVerbatim myprt("TC");
4372  myprt<<someText<<" ChkInTraj failed: inTraj - UseHit mis-match for tj ID "<<tID<<" tj.WorkID "<<tj.WorkID<<" atHits size "<<atHits.size()<<" tHits size "<<tHits.size()<<" in CTP "<<tj.CTP<<"\n";
4373  myprt<<"AlgMods: ";
4374  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
4375  myprt<<"\n";
4376  myprt<<"index inTraj UseHit \n";
4377  for(iht = 0; iht < atHits.size(); ++iht) {
4378  myprt<<"iht "<<iht<<" "<<PrintHit(tjs.fHits[atHits[iht]]);
4379  if(iht < tHits.size()) myprt<<" "<<PrintHit(tjs.fHits[tHits[iht]]);
4380  if(atHits[iht] != tHits[iht]) myprt<<" <<< "<<atHits[iht]<<" != "<<tHits[iht];
4381  myprt<<"\n";
4382  fQuitAlg = true;
4383  } // iht
4384  if(tHits.size() > atHits.size()) {
4385  for(iht = atHits.size(); iht < atHits.size(); ++iht) {
4386  myprt<<"atHits "<<iht<<" "<<PrintHit(tjs.fHits[atHits[iht]])<<"\n";
4387  } // iht
4388  PrintTrajectory("CIT", tjs, tj, USHRT_MAX);
4389  } // tHit.size > atHits.size()
4390  }
4391  // check the VtxID
4392  for(unsigned short end = 0; end < 2; ++end) {
4393  if(tj.VtxID[end] > tjs.vtx.size()) {
4394  mf::LogVerbatim("TC")<<someText<<" ChkInTraj: Bad VtxID "<<tj.ID;
4395  std::cout<<someText<<" ChkInTraj: Bad VtxID "<<tj.ID<<" vtx size "<<tjs.vtx.size()<<"\n";
4396  tj.AlgMod[kKilled] = true;
4397  PrintTrajectory("CIT", tjs, tj, USHRT_MAX);
4398  fQuitAlg = true;
4399  return;
4400  }
4401  } // end
4402  ++itj;
4403  if(fQuitAlg) return;
4404  } // tj
4405 
4406  } // ChkInTraj
4407 
4410  {
4411  // Make clusters from all trajectories in tjs.allTraj
4412 
4413  // Merge hits in trajectory points?
4414  if(fMakeNewHits) MergeTPHits();
4416 
4417  ClusterStore cls;
4418  tjs.tcl.clear();
4419  if(prt) mf::LogVerbatim("TC")<<"MakeAllTrajClusters: tjs.allTraj size "<<tjs.allTraj.size();
4420 
4421  if(tjs.UseAlg[kChkInTraj]) {
4422  fQuitAlg = !InTrajOK(tjs, "MATC");
4423  if(fQuitAlg) {
4424  mf::LogVerbatim("TC")<<"InTrajOK failed in MakeAllTrajClusters";
4425  return;
4426  }
4427  }
4428 
4429  unsigned short itj, endPt0, endPt1;
4430 
4431  // Make one cluster for each trajectory. The indexing of trajectory parents
4432  // should map directly to cluster parents
4433  for(itj = 0; itj < tjs.allTraj.size(); ++itj) {
4434  Trajectory& tj = tjs.allTraj[itj];
4435  if(tj.AlgMod[kKilled]) continue;
4436  // ensure that the endPts are correct
4437  SetEndPoints(tjs, tj);
4438  auto tHits = PutTrajHitsInVector(tj, kUsedHits);
4439  if(tHits.empty()) {
4440  mf::LogWarning("TC")<<"MakeAllTrajClusters: No hits found in trajectory "<<itj<<" so skip it";
4441  PrintTrajectory("MATC", tjs, tj, USHRT_MAX);
4442  continue;
4443  } // error
4444  // special handling for shower Tjs: Sort the hits by distance from the start position
4445  if(tj.AlgMod[kShowerTj]) {
4446  std::vector<SortEntry> sortVec(tHits.size());
4447  SortEntry sortEntry;
4448  std::array<float, 2> hpos;
4449  for(unsigned short ii = 0; ii < tHits.size(); ++ii) {
4450  unsigned int iht = tHits[ii];
4451  hpos[0] = tjs.fHits[iht].ArtPtr->WireID().Wire;
4452  hpos[1] = tjs.fHits[iht].PeakTime * tjs.UnitsPerTick;
4453  sortEntry.index = ii;
4454  sortEntry.val = PosSep2(hpos, tj.Pts[0].Pos);
4455  sortVec[ii] = sortEntry;
4456  } // ii
4457  std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
4458  // make a temp vector
4459  std::vector<unsigned int> tmp(sortVec.size());
4460  // enter the sorted hits
4461  for(unsigned short ii = 0; ii < sortVec.size(); ++ii) tmp[ii] = tHits[sortVec[ii].index];
4462  tHits = tmp;
4463  } // showerTj
4464  // count AlgMod bits
4465  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) ++fAlgModCount[ib];
4466  cls.ID = tj.ID;
4467  // assign shower clusters a negative ID
4468  if(tj.AlgMod[kShowerTj]) cls.ID = -cls.ID;
4469  if( ((tjs.ShowerTag[0] == 1) || (tjs.ShowerTag[0] == 3)) && tj.AlgMod[kShowerLike]) cls.ID = -cls.ID;
4470  cls.CTP = tj.CTP;
4471  cls.PDGCode = tj.PDGCode;
4472  endPt0 = tj.EndPt[0];
4473  cls.BeginWir = tj.Pts[endPt0].Pos[0];
4474  cls.BeginTim = tj.Pts[endPt0].Pos[1] / tjs.UnitsPerTick;
4475  cls.BeginAng = tj.Pts[endPt0].Ang;
4476  cls.BeginChg = tj.Pts[endPt0].Chg;
4477  cls.BeginVtx = tj.VtxID[0]-1;
4478  endPt1 = tj.EndPt[1];
4479  cls.EndWir = tj.Pts[endPt1].Pos[0];
4480  cls.EndTim = tj.Pts[endPt1].Pos[1] / tjs.UnitsPerTick;
4481  cls.EndAng = tj.Pts[endPt1].Ang;
4482  cls.EndChg = tj.Pts[endPt1].Chg;
4483  cls.EndVtx = tj.VtxID[1]-1;
4484  cls.tclhits = tHits;
4485  // Set the traj info
4486  tj.ClusterIndex = tjs.tcl.size();
4487  tjs.tcl.push_back(cls);
4488  } // itj
4489 
4490  } // MakeAllTrajClusters
4491 
4492 
4495  {
4496  // Find missing 2D vertices in a plane due to a mis-reconstructed Tj
4497 
4498  if(!tjs.UseAlg[kMisdVxTj]) return;
4499 
4500  bool prt = (debug.Plane >= 0 && debug.Tick == 77777);
4501  if(prt) mf::LogVerbatim("TC")<<"Inside FMVTjs "<<tjs.vtx3.size();
4502 
4503  float maxdoca = 6;
4504  for(unsigned short iv3 = 0; iv3 < tjs.vtx3.size(); ++iv3) {
4505  Vtx3Store& vx3 = tjs.vtx3[iv3];
4506  // ignore obsolete vertices
4507  if(vx3.ID == 0) continue;
4508  if(vx3.TPCID != tpcid) continue;
4509  // check for a completed 3D vertex
4510  if(vx3.Wire < 0) continue;
4511  unsigned short mPlane = USHRT_MAX;
4512  unsigned short ntj_1stPlane = USHRT_MAX;
4513  unsigned short ntj_2ndPlane = USHRT_MAX;
4514  for(unsigned short plane = 0; plane < tjs.NumPlanes; ++plane) {
4515  if(vx3.Vx2ID[plane] > 0) {
4516  auto& vx2 = tjs.vtx[vx3.Vx2ID[plane] - 1];
4517  if(ntj_1stPlane == USHRT_MAX) {
4518  ntj_1stPlane = vx2.NTraj;
4519  } else {
4520  ntj_2ndPlane = vx2.NTraj;
4521  }
4522  continue;
4523  }
4524  mPlane = plane;
4525  } // plane
4526  if(mPlane == USHRT_MAX) continue;
4527  CTP_t mCTP = EncodeCTP(vx3.TPCID.Cryostat, vx3.TPCID.TPC, mPlane);
4528  // X position of the purported missing vertex
4529  // A TP for the missing 2D vertex
4530  TrajPoint tp;
4531  tp.Pos[0] = vx3.Wire;
4532  tp.Pos[1] = tjs.detprop->ConvertXToTicks(vx3.X, mPlane, vx3.TPCID.TPC, vx3.TPCID.Cryostat) * tjs.UnitsPerTick;
4533  std::vector<int> tjIDs;
4534  std::vector<unsigned short> tj2Pts;
4535  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
4536  if(tjs.allTraj[itj].CTP != mCTP) continue;
4537  if(tjs.allTraj[itj].AlgMod[kKilled]) continue;
4538  if(tjs.allTraj[itj].Pts.size() < 6) continue;
4539  if(tjs.allTraj[itj].AlgMod[kComp3DVx]) continue;
4540  float doca = maxdoca;
4541  // find the closest distance between the vertex and the trajectory
4542  unsigned short closePt = 0;
4543  TrajPointTrajDOCA(tjs, tp, tjs.allTraj[itj], closePt, doca);
4544  if(closePt > tjs.allTraj[itj].EndPt[1]) continue;
4545  if(prt) mf::LogVerbatim("TC")<<"FMVTjs 3V"<<vx3.ID<<" candidate T"<<tjs.allTraj[itj].ID<<" closePT "<<closePt<<" doca "<<doca;
4546  tjIDs.push_back(tjs.allTraj[itj].ID);
4547  tj2Pts.push_back(closePt);
4548  } // itj
4549  // handle the case where there are one or more TJs with TPs near the ends
4550  // that make a vertex (a failure by Find2DVertices)
4551  if(tjIDs.empty()) continue;
4552  if(prt) mf::LogVerbatim("TC")<<" 3V"<<vx3.ID<<" mPlane "<<mPlane<<" ntj_1stPlane "<<ntj_1stPlane<<" ntj_2ndPlane "<<ntj_2ndPlane;
4553  } // iv3
4554  } // FindMissedVxTjs
4555 
4558  {
4559  // Look for vertex trajectories in all vertices in CTP
4560  if(!tjs.UseAlg[kVtxTj]) return;
4561 
4562  for(auto& vx2 : tjs.vtx) {
4563  if(vx2.ID == 0) continue;
4564  if(vx2.CTP != inCTP) continue;
4565  if(vx2.Stat[kVtxTrjTried]) continue;
4566  FindVtxTraj(vx2);
4567  } // vx2
4568 
4569  } // FindVtxTjs
4570 
4573  {
4574  // Look for available hits in the vicinity of this vertex and try to make
4575  // a vertex trajectory from them
4576 
4577  if(!tjs.UseAlg[kVtxTj]) return;
4578 
4579  if(vx2.Stat[kVtxTrjTried]) return;
4580  // ignore low score
4581  if(vx2.Score < tjs.Vertex2DCuts[7]) return;
4582 
4583  std::array<int, 2> wireWindow;
4584  std::array<float, 2> timeWindow;
4585 
4586  // on the first try we look for small angle trajectories which will have hits
4587  // with a large wire window and a small time window
4588  // Vertex2DCuts fcl input usage
4589  // 0 User definition of a short Tj => max number of Tj points
4590  // 1 max separation between a vertex and the start of a trajectory for a user-defined short Tj
4591  // 2 max separation for a user-defined long Tj
4592  // 3 max position pull when attaching a Tj to a vertex
4593  // 4 max position error for creating a Tj or attaching Tjs to an existing vertex
4594  // 5 Min MCSMom of Tjs that can be used to create a vertex
4595  // 6 min frac of Points/Wire between a vtx and a Tj. Ideally one if the efficiency is good
4596  // 7 min Score
4597  // 8 ID of a vertex for printing special debugging information
4598  wireWindow[0] = std::nearbyint(vx2.Pos[0] - tjs.Vertex2DCuts[2]);
4599  wireWindow[1] = std::nearbyint(vx2.Pos[0] + tjs.Vertex2DCuts[2]);
4600  timeWindow[0] = vx2.Pos[1] - 5;
4601  timeWindow[1] = vx2.Pos[1] + 5;
4602 
4603  geo::PlaneID planeID = DecodeCTP(vx2.CTP);
4604  unsigned short ipl = planeID.Plane;
4605 
4606  if(prt) mf::LogVerbatim("TC")<<"inside FindVtxTraj "<<vx2.ID<<" Window "<<wireWindow[0]<<" "<<wireWindow[1]<<" "<<timeWindow[0]<<" "<<timeWindow[1]<<" in plane "<<ipl;
4607 
4608  // find nearby available hits
4609  bool hitsNear;
4610  std::vector<unsigned int> closeHits = FindCloseHits(tjs, wireWindow, timeWindow, ipl, kUnusedHits, true, hitsNear);
4611  if(closeHits.empty()) return;
4612  if(prt) {
4613  mf::LogVerbatim myprt("TC");
4614  myprt<<"closeHits";
4615  for(auto& iht : closeHits) myprt<<" "<<PrintHit(tjs.fHits[iht]);
4616  }
4617  // sort by distance from the vertex
4618  std::vector<SortEntry> sortVec(closeHits.size());
4619  SortEntry sortEntry;
4620  for(unsigned short ii = 0; ii < closeHits.size(); ++ii) {
4621  unsigned int iht = closeHits[ii];
4622  float dw = tjs.fHits[iht].ArtPtr->WireID().Wire - vx2.Pos[0];
4623  float dt = tjs.UnitsPerTick * tjs.fHits[iht].PeakTime - vx2.Pos[1];
4624  float d2 = dw * dw + dt * dt;
4625  sortEntry.index = ii;
4626  sortEntry.val = d2;
4627  sortVec[ii] = sortEntry;
4628  } // ii
4629  std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
4630  unsigned int vWire = std::nearbyint(vx2.Pos[0]);
4631  int vTick = vx2.Pos[1]/tjs.UnitsPerTick;
4632  if(prt) PrintHeader("FVT");
4633  for(unsigned short ii = 0; ii < closeHits.size(); ++ii) {
4634  unsigned int iht = closeHits[sortVec[ii].index];
4635  if(tjs.fHits[iht].InTraj > 0) continue;
4636  // the direction will be poorly defined if a hit is very close to the vertex and it is in this list.
4637  // Ignore these hits
4638  if(tjs.fHits[iht].ArtPtr->WireID().Wire == vWire) {
4639  // on the vertex wire. Check for a close time
4640  if(abs(tjs.fHits[iht].PeakTime - vTick) < 10) continue;
4641  } // hit on vtx wire
4642  float toWire = tjs.fHits[iht].ArtPtr->WireID().Wire;
4643  float toTick = tjs.fHits[iht].PeakTime;
4644  // assume the last pass and fix it later after the angle is calculated
4645  unsigned short pass = fMinPts.size() - 1;
4646  Trajectory tj;
4647  if(!StartTraj(tj, vx2.Pos[0], vx2.Pos[1]/tjs.UnitsPerTick, toWire, toTick, vx2.CTP, pass)) continue;
4648  // ensure that the first TP is good
4649  if(tj.Pts[0].Pos[0] < 0) continue;
4650  // std::cout<<"fvt "<<vx2.ID<<" "<<tj.ID<<" vtx0 "<<vx2.Pos[0]<<" hit "<<PrintHit(tjs.fHits[iht])<<" StepDir "<<tj.StepDir<<"\n";
4651  tj.VtxID[0] = vx2.ID;
4652  TrajPoint& tp = tj.Pts[0];
4653  // Move the Pt to the hit
4654  MoveTPToWire(tp, toWire);
4655  // attach the hit
4656  tp.Hits.push_back(iht);
4657  tp.UseHit[tp.Hits.size()-1] = true;
4658  tjs.fHits[iht].InTraj = tj.ID;
4659  tp.UseHit[tp.Hits.size()-1] = false;
4660  if(prt) PrintTrajPoint("FVT", tjs, 0, tj.StepDir, tj.Pass, tp);
4661  // Step away and see what happens
4662  StepCrawl(tj);
4663  // check for a major failure
4664  if(fQuitAlg) return;
4665  // Check the quality of the trajectory
4666  CheckTraj(tj);
4667  if(!fGoodTraj || NumPtsWithCharge(tjs, tj, true) < 2) {
4668  if(prt) mf::LogVerbatim("TC")<<" xxxxxxx Not enough points "<<NumPtsWithCharge(tjs, tj, true)<<" minimum "<<fMinPts[tj.Pass]<<" or !fGoodTraj";
4669  ReleaseHits(tjs, tj);
4670  continue;
4671  }
4672 // if(prt) prt = false;
4673  tj.AlgMod[kVtxTj] = true;
4674  fQuitAlg = !StoreTraj(tjs, tj);
4675  if(tjs.UseAlg[kChkInTraj]) {
4676  fQuitAlg = !InTrajOK(tjs, "FVT");
4677  if(fQuitAlg) {
4678  mf::LogVerbatim("TC")<<"InTrajOK failed in FindVtxTraj";
4679  return;
4680  }
4681  }
4682  if(prt) mf::LogVerbatim("TC")<<"FindVtxTraj: calling StoreTraj with npts "<<tj.EndPt[1];
4683  } // ii
4684 
4685  // Flag this as tried so we don't try again
4686  vx2.Stat[kVtxTrjTried] = true;
4687  } // FindVtxTraj
4688 
4690  void TrajClusterAlg::GetHitMultiplet(unsigned int theHit, std::vector<unsigned int>& hitsInMultiplet)
4691  {
4692  unsigned short localIndex;
4693  GetHitMultiplet(theHit, hitsInMultiplet, localIndex);
4694  } // GetHitMultiplet
4695 
4697  void TrajClusterAlg::GetHitMultiplet(unsigned int theHit, std::vector<unsigned int>& hitsInMultiplet, unsigned short& localIndex)
4698  {
4699  hitsInMultiplet.clear();
4700  localIndex = 0;
4701  if(theHit > tjs.fHits.size() - 1) return;
4702  if(tjs.fHits[theHit].InTraj == INT_MAX) return;
4703  hitsInMultiplet.resize(1);
4704  hitsInMultiplet[0] = theHit;
4705 
4706  unsigned int theWire = tjs.fHits[theHit].ArtPtr->WireID().Wire;
4707  unsigned short ipl = tjs.fHits[theHit].ArtPtr->WireID().Plane;
4708  float theTime = tjs.fHits[theHit].PeakTime;
4709  float theRMS = tjs.fHits[theHit].RMS;
4710  float narrowHitCut = 1.5 * tjs.AveHitRMS[ipl];
4711  bool theHitIsNarrow = (theRMS < narrowHitCut);
4712  float maxPeak = tjs.fHits[theHit].PeakAmplitude;
4713  unsigned short imTall = theHit;
4714  unsigned short nNarrow = 0;
4715  if(theHitIsNarrow) nNarrow = 1;
4716 /*
4717  bool mprt = (theHit == 425);
4718  if(mprt) {
4719  mf::LogVerbatim("TC")<<"GetHitMultiplet theHit "<<theHit<<" "<<PrintHit(tjs.fHits[theHit])<<" RMS "<<tjs.fHits[theHit].RMS<<" aveRMS "<<tjs.AveHitRMS[ipl]<<" Amp "<<(int)tjs.fHits[theHit].PeakAmplitude;
4720  }
4721 */
4722  // look for hits < theTime but within hitSep
4723  if(theHit > 0) {
4724  for(unsigned int iht = theHit - 1; iht != 0; --iht) {
4725  if(tjs.fHits[iht].ArtPtr->WireID().Wire != theWire) break;
4726  if(tjs.fHits[iht].ArtPtr->WireID().Plane != ipl) break;
4727  float hitSep = fMultHitSep * theRMS;
4728  if(tjs.fHits[iht].RMS > theRMS) {
4729  hitSep = fMultHitSep * tjs.fHits[iht].RMS;
4730  theRMS = tjs.fHits[iht].RMS;
4731  }
4732  float dTick = std::abs(tjs.fHits[iht].PeakTime - theTime);
4733  if(dTick > hitSep) break;
4734 // if(mprt) mf::LogVerbatim("TC")<<" iht- "<<iht<<" "<<tjs.fHits[iht].WireID.Plane<<":"<<PrintHit(tjs.fHits[iht])<<" RMS "<<tjs.fHits[iht].RMS<<" dTick "<<dTick<<" hitSep "<<hitSep<<" Amp "<<(int)tjs.fHits[iht].PeakAmplitude;
4735  hitsInMultiplet.push_back(iht);
4736  if(tjs.fHits[iht].RMS < narrowHitCut) ++nNarrow;
4737  if(tjs.fHits[iht].PeakAmplitude > maxPeak) {
4738  maxPeak = tjs.fHits[iht].PeakAmplitude;
4739  imTall = iht;
4740  }
4741  theTime = tjs.fHits[iht].PeakTime;
4742  if(iht == 0) break;
4743  } // iht
4744  } // iht > 0
4745  localIndex = hitsInMultiplet.size() - 1;
4746  // reverse the order so that hitsInMuliplet will be
4747  // returned in increasing time order
4748  if(hitsInMultiplet.size() > 1) std::reverse(hitsInMultiplet.begin(), hitsInMultiplet.end());
4749  // look for hits > theTime but within hitSep
4750  theTime = tjs.fHits[theHit].PeakTime;
4751  theRMS = tjs.fHits[theHit].RMS;
4752  for(unsigned int iht = theHit + 1; iht < tjs.fHits.size(); ++iht) {
4753  if(tjs.fHits[iht].ArtPtr->WireID().Wire != theWire) break;
4754  if(tjs.fHits[iht].ArtPtr->WireID().Plane != ipl) break;
4755  if(tjs.fHits[iht].InTraj == INT_MAX) continue;
4756  float hitSep = fMultHitSep * theRMS;
4757  if(tjs.fHits[iht].RMS > theRMS) {
4758  hitSep = fMultHitSep * tjs.fHits[iht].RMS;
4759  theRMS = tjs.fHits[iht].RMS;
4760  }
4761  float dTick = std::abs(tjs.fHits[iht].PeakTime - theTime);
4762  if(dTick > hitSep) break;
4763 // if(mprt) mf::LogVerbatim("TC")<<" iht+ "<<iht<<" "<<PrintHit(tjs.fHits[iht])<<" dTick "<<dTick<<" RMS "<<tjs.fHits[iht].RMS<<" "<<hitSep<<" Amp "<<(int)tjs.fHits[iht].PeakAmplitude;
4764  hitsInMultiplet.push_back(iht);
4765  if(tjs.fHits[iht].RMS < narrowHitCut) ++nNarrow;
4766  if(tjs.fHits[iht].PeakAmplitude > maxPeak) {
4767  maxPeak = tjs.fHits[iht].PeakAmplitude;
4768  imTall = iht;
4769  }
4770  theTime = tjs.fHits[iht].PeakTime;
4771  } // iht
4772 /*
4773  if(mprt) {
4774  mf::LogVerbatim myprt("TC");
4775  myprt<<" return ";
4776  for(auto iht : hitsInMultiplet) myprt<<" "<<PrintHit(tjs.fHits[iht]);
4777  }
4778 */
4779  if(hitsInMultiplet.size() == 1) return;
4780 
4781  if(hitsInMultiplet.size() > 16) {
4782  // Found > 16 hits in a multiplet which would be bad for UseHit. Truncate it
4783  hitsInMultiplet.resize(16);
4784  return;
4785  }
4786 
4787  // Don't make a multiplet that includes a tall narrow hit with short fat hits
4788  if(nNarrow == hitsInMultiplet.size()) return;
4789  if(nNarrow == 0) return;
4790 
4791  if(theHitIsNarrow && theHit == imTall) {
4792 // if(mprt) mf::LogVerbatim("TC")<<" theHit is narrow and tall. Use only it";
4793  // theHit is narrow and it is the highest amplitude hit in the multiplet. Ignore any
4794  // others that are short and fat
4795  auto tmp = hitsInMultiplet;
4796  tmp.resize(1);
4797  tmp[0] = theHit;
4798  hitsInMultiplet = tmp;
4799  } else {
4800  // theHit is not narrow and it is not the tallest. Ignore a single hit if it is
4801  // the tallest and narrow
4802 // if(mprt) mf::LogVerbatim("TC")<<" theHit is not narrow or tall";
4803  if(tjs.fHits[imTall].RMS < narrowHitCut) {
4804  unsigned short killMe = 0;
4805  for(unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
4806  if(hitsInMultiplet[ii] == imTall) {
4807  killMe = ii;
4808  break;
4809  }
4810  } // ii
4811  hitsInMultiplet.erase(hitsInMultiplet.begin() + killMe);
4812  } // tjs.fHits[imTall].RMS < narrowHitCut
4813  } // narrow / tall test
4814 
4815  } // GetHitMultiplet
4816 
4818  std::vector<recob::Hit> TrajClusterAlg::YieldHits()
4819  {
4820  // Create the final recob::hits and return them
4821  std::vector<recob::Hit> tmp;
4822  tmp.reserve(tjs.fHits.size());
4823  for(auto& tcHit : tjs.fHits) {
4824  geo::PlaneID planeID = geo::PlaneID(tcHit.ArtPtr->WireID().Cryostat, tcHit.ArtPtr->WireID().TPC, tcHit.ArtPtr->WireID().Plane);
4825  raw::ChannelID_t channel = tjs.geom->PlaneWireToChannel((int)tcHit.ArtPtr->WireID().Plane, (int)tcHit.ArtPtr->WireID().Wire, (int)tcHit.ArtPtr->WireID().TPC, (int)tcHit.ArtPtr->WireID().Cryostat);
4826  tmp.emplace_back(channel,
4827  tcHit.StartTick, tcHit.EndTick,
4828  tcHit.PeakTime, tcHit.SigmaPeakTime,
4829  tcHit.RMS,
4830  tcHit.PeakAmplitude, tcHit.SigmaPeakAmp,
4831  tcHit.Integral, tcHit.Integral, tcHit.SigmaIntegral,
4832  tcHit.Multiplicity, tcHit.LocalIndex,
4833  tcHit.GoodnessOfFit, tcHit.NDOF,
4834  tjs.geom->View(channel),
4835  tjs.geom->SignalType(planeID),
4836  tcHit.ArtPtr->WireID()
4837  );
4838  } // tcHit
4839  return tmp;
4840  } // YieldHits
4841 
4843  bool TrajClusterAlg::EraseHit(const unsigned int& delHit)
4844  {
4845  // Erases delHit and makes corrections to allTraj and WireHitRange
4846  if(delHit > tjs.fHits.size() - 1) {
4847  mf::LogWarning("TC")<<"Trying to erase an invalid hit";
4848  return false;
4849  }
4850  // erase the hit
4851  tjs.fHits.erase(tjs.fHits.begin()+delHit);
4852  // Correct WireHitRange
4853  int idelHit = delHit;
4854  for(unsigned short ipl = 0; ipl < tjs.NumPlanes; ++ipl) {
4855  for(unsigned int wire = tjs.FirstWire[ipl]; wire < tjs.LastWire[ipl]; ++wire) {
4856  // ignore wires with no hits or dead
4857  if(tjs.WireHitRange[ipl][wire].first < 0) continue;
4858  if(idelHit > 0 && tjs.WireHitRange[ipl][wire].first > idelHit) --tjs.WireHitRange[ipl][wire].first;
4859  if(tjs.WireHitRange[ipl][wire].second > idelHit) --tjs.WireHitRange[ipl][wire].second;
4860  // Deal with the situation where this is the only hit on a wire
4861  int firstHit = tjs.WireHitRange[ipl][wire].first;
4862  int lastHit = tjs.WireHitRange[ipl][wire].second - 1;
4863  if(lastHit <= firstHit) {
4864  // erasing the only hit on this wire
4865  tjs.WireHitRange[ipl][wire].first = -2;
4866  tjs.WireHitRange[ipl][wire].second = -2;
4867  // skip checking
4868  continue;
4869  }
4870  } // wire
4871  } // ipl
4872 
4873  // do another sanity check
4874  if(!CheckWireHitRange(tjs)) return false;
4875 
4876  // now fix the Trajectory point hit -> tjs.fHits.InTraj association. The first step is to
4877  // remove any use of delHit in all trajectory points. The second is to remove any trajectory point that
4878  // uses only delHit to define the hit position and is therefore no longer valid
4879  for(auto& tj : tjs.allTraj) {
4880  unsigned short killPt = USHRT_MAX;
4881  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
4882  TrajPoint& tp = tj.Pts[ipt];
4883  unsigned short killii = USHRT_MAX;
4884  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
4885  if(tp.Hits[ii] == delHit) {
4886  // delHit is used in this TP so we need to remove it
4887  killii = ii;
4888  } else if(tp.Hits[ii] > delHit) {
4889  // delHit comes later in the hit collection so we need to simply correct it
4890  --tp.Hits[ii];
4891  }
4892  } // ii
4893  if(killii != USHRT_MAX) {
4894  // We need to erase the reference to this hit in the TP
4895  tp.Hits.erase(tp.Hits.begin() + killii);
4896  // shift UseHit being careful not to go outside the bounds
4897  unsigned short maxSize = tp.Hits.size();
4898  if(maxSize == 16) maxSize = 15;
4899  for(unsigned short ii = killii; ii < maxSize; ++ii) tp.UseHit[ii] = tp.UseHit[ii + 1];
4900  // Flag this TP for deletion if there are no other hits
4901  if(tp.Hits.empty()) killPt = ipt;
4902  } // killii != USHRT_MAX
4903  } // ipt
4904  // delHit was the only hit used in this TP and it was erased so delete the TP
4905  if(killPt != USHRT_MAX) {
4906  tj.Pts.erase(tj.Pts.begin() + killPt);
4907  SetEndPoints(tjs, tj);
4908  }
4909  } // tj
4910 
4911  return true;
4912  } // EraseHit
4913 
4915  void TrajClusterAlg::DefineHit(TCHit& tcHit, CTP_t& hitCTP, unsigned int& hitWire)
4916  {
4917  // Defines the hit WireID, channel, etc using hitCTP and hitWire
4918  geo::PlaneID planeID = DecodeCTP(hitCTP);
4919  tcHit.ArtPtr->WireID() = geo::WireID(planeID, hitWire);
4920 // hitCTP.Channel = tjs.geom->PlaneWireToChannel((int)planeID.Plane,(int)hitWire,(int)planeID.TPC,(int)planeID.Cryostat);
4921  } // DefineHit
4922 
4924  unsigned int TrajClusterAlg::CreateHit(TCHit tcHit)
4925  {
4926  // Creates a hit in tjs.fHits using the supplied information. Returns UINT_MAX if there is failure.
4927  // Returns the index of the newly created hit.
4928  unsigned short newHitPlane = tcHit.ArtPtr->WireID().Plane;
4929  unsigned int newHitWire = tcHit.ArtPtr->WireID().Wire;
4930  // don't try to create a hit on a dead wire
4931  if(tjs.WireHitRange[newHitPlane][newHitWire].first == -1) return UINT_MAX;
4932 
4933  // Figure out where to put it
4934  unsigned int newHitIndex = UINT_MAX;
4935  if(tjs.WireHitRange[newHitPlane][newHitWire].first == -2) {
4936  // We want to put this hit on a wire that currently has none. Find the next wire that has a hit.
4937  // First look in the plane in which we want to put it
4938  for(unsigned int wire = newHitWire + 1; wire < tjs.NumWires[newHitPlane]; ++wire) {
4939  if(tjs.WireHitRange[newHitPlane][wire].first >= 0) {
4940  newHitIndex = tjs.WireHitRange[newHitPlane][wire].first;
4941  break;
4942  }
4943  } // wire
4944  // if not found in this plane look in the rest of the planes
4945  if(newHitIndex == UINT_MAX) {
4946  for(unsigned short ipl = newHitPlane + 1; ipl < tjs.NumPlanes; ++ipl) {
4947  for(unsigned int wire = tjs.FirstWire[ipl]; wire < tjs.LastWire[ipl]; ++wire) {
4948  if(tjs.WireHitRange[ipl][wire].first >= 0) {
4949  newHitIndex = tjs.WireHitRange[ipl][wire].first;
4950  break;
4951  }
4952  } // wire
4953  if(newHitIndex != UINT_MAX) break;
4954  } // ipl
4955  } // newHitIndex == UINT_MAX
4956  } else {
4957  // Hits exist on this wire
4958  unsigned int firstHit = tjs.WireHitRange[newHitPlane][newHitWire].first;
4959  unsigned int lastHit = tjs.WireHitRange[newHitPlane][newHitWire].second - 1;
4960  if(tcHit.PeakTime <= tjs.fHits[firstHit].PeakTime) {
4961  // new hit is earlier in time so it should be inserted before firstHit
4962  newHitIndex = firstHit;
4963  } else if(tcHit.PeakTime > tjs.fHits[lastHit].PeakTime) {
4964  // new hit is later so it should inserted after lastHit
4965  newHitIndex = lastHit + 1;
4966  } else {
4967  // new hit is somewhere in the middle
4968  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
4969  if(tcHit.PeakTime > tjs.fHits[iht].PeakTime && tcHit.PeakTime <= tjs.fHits[iht+1].PeakTime) {
4970  // found it
4971  newHitIndex = iht + 1;
4972  break;
4973  }
4974  } // iht
4975  } // new hit in the middle
4976  } // Hits exist on this wire
4977 
4978  // this shouldn't be possible
4979  if(newHitIndex == UINT_MAX) {
4980 // std::cout<<"CreateHit: Failed to find newHitIndex for new hit "<<PrintHit(tcHit)<<"\n";
4981  return newHitIndex;
4982  }
4983 
4984  // insert the hit
4985  tjs.fHits.insert(tjs.fHits.begin() + newHitIndex, tcHit);
4986 
4987  // Correct WireHitRange
4988 
4989  // Put the hit on a wire with no existing hits
4990  if(tjs.WireHitRange[newHitPlane][newHitWire].first == -2) {
4991  tjs.WireHitRange[newHitPlane][newHitWire].first = newHitIndex;
4992  tjs.WireHitRange[newHitPlane][newHitWire].second = newHitIndex + 1;
4993  } else {
4994  // This wire has hits, one of which is the new hits, so only correct the last hit
4995  ++tjs.WireHitRange[newHitPlane][newHitWire].second;
4996  }
4997 
4998  // correct the hit ranges in newHitPlane on wires after newHitWire
4999  for(unsigned int wire = newHitWire + 1; wire < tjs.LastWire[newHitPlane]; ++wire) {
5000  // dead wire
5001  if(tjs.WireHitRange[newHitPlane][wire].first < 0) continue;
5002  ++tjs.WireHitRange[newHitPlane][wire].first;
5003  ++tjs.WireHitRange[newHitPlane][wire].second;
5004 /*
5005  // check the hits
5006  int firstHit = tjs.WireHitRange[newHitPlane][wire].first;
5007  int lastHit = tjs.WireHitRange[newHitPlane][wire].second - 1;
5008  if(tjs.fHits[firstHit].ArtPtr->WireID().Plane != newHitPlane || tjs.fHits[firstHit].ArtPtr->WireID().Wire != wire) {
5009  std::cout<<"WireHitRange1 screwup on firstHit "<<tjs.fHits[firstHit].ArtPtr->WireID().Plane<<":"<<tjs.fHits[firstHit].ArtPtr->WireID().Wire;
5010  std::cout<<" != "<<newHitPlane<<":"<<wire<<"\n";
5011  exit(1);
5012  } // error checking
5013  if(tjs.fHits[lastHit].ArtPtr->WireID().Plane != newHitPlane || tjs.fHits[lastHit].ArtPtr->WireID().Wire != wire) {
5014  std::cout<<"WireHitRange1 screwup on lastHit "<<tjs.fHits[lastHit].ArtPtr->WireID().Plane<<":"<<tjs.fHits[lastHit].ArtPtr->WireID().Wire;
5015  std::cout<<" != "<<newHitPlane<<":"<<wire<<"\n";
5016  exit(1);
5017  } // error checking
5018 */
5019  } // wire
5020 
5021  // correct the hit ranges for the later planes
5022  for(unsigned short ipl = newHitPlane + 1; ipl < tjs.NumPlanes; ++ipl) {
5023  for(unsigned int wire = tjs.FirstWire[ipl]; wire < tjs.LastWire[ipl]; ++wire) {
5024  if(tjs.WireHitRange[ipl][wire].first < 0) continue;
5025  ++tjs.WireHitRange[ipl][wire].first;
5026  ++tjs.WireHitRange[ipl][wire].second;
5027  // check the hits
5028  int firstHit = tjs.WireHitRange[ipl][wire].first;
5029  int lastHit = tjs.WireHitRange[ipl][wire].second - 1;
5030  if(tjs.fHits[firstHit].ArtPtr->WireID().Plane != ipl || tjs.fHits[firstHit].ArtPtr->WireID().Wire != wire) {
5031  std::cout<<"WireHitRange2 screwup on firstHit "<<tjs.fHits[firstHit].ArtPtr->WireID().Plane<<":"<<tjs.fHits[firstHit].ArtPtr->WireID().Wire;
5032  std::cout<<" != "<<ipl<<":"<<wire<<"\n";
5033  exit(1);
5034  } // error checking
5035  if(tjs.fHits[lastHit].ArtPtr->WireID().Plane != ipl || tjs.fHits[lastHit].ArtPtr->WireID().Wire != wire) {
5036  std::cout<<"WireHitRange2 screwup on lastHit "<<tjs.fHits[lastHit].ArtPtr->WireID().Plane<<":"<<tjs.fHits[lastHit].ArtPtr->WireID().Wire;
5037  std::cout<<" != "<<ipl<<":"<<wire<<"\n";
5038  exit(1);
5039  } // error checking
5040  } // wire
5041  } // ipl
5042 
5043 
5044  if(!CheckWireHitRange(tjs)) return UINT_MAX;
5045 
5046  // now correct the hit indices in the trajectories
5047  for(auto& tj : tjs.allTraj) {
5048  for(auto& tp : tj.Pts) {
5049  for(unsigned short iht = 0; iht < tp.Hits.size(); ++iht) {
5050  if(tp.Hits[iht] >= newHitIndex) ++tp.Hits[iht];
5051 /*
5052  if(tp.Hits.size() == 1 && tp.Chg > 0 && tjs.fHits[tp.Hits[iht]].ArtPtr->WireID().Wire != std::nearbyint(tp.Pos[0])) {
5053  std::cout<<" Create index problem tj.ID "<<tj.ID<<" iht "<<iht<<" newHitIndex "<<newHitIndex;
5054  std::cout<<" hit "<<PrintHit(tjs.fHits[tp.Hits[iht]])<<" Pos "<<PrintPos(tjs, tp)<<"\n";
5055  exit(1);
5056  }
5057 */
5058  } // iht
5059  } // tp
5060  }
5061 
5062  return newHitIndex;
5063 
5064  } // CreateHit
5065 
5068  {
5069 
5070  // Merge all hits that are used in one TP into a single hit
5071  // Make a list of hits that are slated for deletion
5072  std::vector<unsigned int> delHits;
5073  for(unsigned short itj = 0; itj < tjs.allTraj.size(); ++itj) {
5074  if(tjs.allTraj[itj].AlgMod[kKilled]) continue;
5075  Trajectory& tj = tjs.allTraj[itj];
5076  // ignore shower Tj hits
5077  if(tj.AlgMod[kShowerTj]) continue;
5078  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
5079  TrajPoint& tp = tj.Pts[ipt];
5080  if(NumHitsInTP(tp, kUsedHits) < 2) continue;
5081  // Make a list of the old hits on this TP before doing anything invasive
5082  std::vector<unsigned int> oldHits;
5083  // get some info so we can calculate the RMS
5084  raw::TDCtick_t loTick = INT_MAX;
5085  raw::TDCtick_t hiTick = 0;
5086  float mChg = 0;
5087  float mTick = 0;
5088  // estimate the uncertainties
5089  float mSigmaPeakAmp = 0;
5090  float mSigmaPeakTime = 0;
5091  float mSigmaIntegral = 0;
5092  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
5093  if(!tp.UseHit[ii]) continue;
5094  unsigned int iht = tp.Hits[ii];
5095  oldHits.push_back(iht);
5096  if(tjs.fHits[iht].StartTick < loTick) loTick = tjs.fHits[iht].StartTick;
5097  if(tjs.fHits[iht].EndTick > hiTick) hiTick = tjs.fHits[iht].EndTick;
5098  mChg += tjs.fHits[iht].Integral;
5099  mTick += tjs.fHits[iht].Integral * tjs.fHits[iht].PeakTime;
5100  mSigmaPeakAmp += tjs.fHits[iht].Integral * tjs.fHits[iht].SigmaPeakAmp;
5101  mSigmaPeakTime += tjs.fHits[iht].Integral * tjs.fHits[iht].SigmaPeakTime;
5102  mSigmaIntegral += tjs.fHits[iht].Integral * tjs.fHits[iht].SigmaIntegral;
5103  } // ii
5104  mTick /= mChg;
5105  if(mTick < 0) mTick = 0;
5106  mSigmaPeakAmp /= mChg;
5107  mSigmaPeakTime /= mChg;
5108  mSigmaIntegral /= mChg;
5109  // make a temporary signal waveform vector
5110  std::vector<float> signal(hiTick - loTick, 0);
5111  // fill it with the hit shapes
5112  for(auto& iht : oldHits) {
5113  float& peakTime = tjs.fHits[iht].PeakTime;
5114  float& amp = tjs.fHits[iht].PeakAmplitude;
5115  float& rms = tjs.fHits[iht].RMS;
5116  // add charge in the range +/- 3 sigma
5117  short loTime = (short)(peakTime - 3 * rms);
5118  if(loTime < loTick) loTime = loTick;
5119  short hiTime = (short)(peakTime + 3 * rms);
5120  if(hiTime > hiTick) hiTime = hiTick;
5121  for(short time = loTime; time < hiTime; ++time) {
5122  unsigned short indx = time - loTick;
5123  if(indx > signal.size() - 1) continue;
5124  float arg = (time - peakTime) / rms;
5125  signal[indx] += amp * exp(-0.5 * arg * arg);
5126  } // time
5127  } // iht
5128  // aveIndx is the index of the charge-weighted average in the signal vector
5129  float aveIndx = (mTick - loTick);
5130  // find the merged hit RMS
5131  float mRMS = 0;
5132  for(unsigned short indx = 0; indx < signal.size(); ++indx) {
5133  float dindx = indx - aveIndx;
5134  mRMS += signal[indx] * dindx * dindx;
5135  } // indx
5136  mRMS = std::sqrt(mRMS / mChg);
5137  // Modify the first hit in the list
5138  unsigned int mht = oldHits[0];
5139  tjs.fHits[mht].PeakTime = mTick;
5140  tjs.fHits[mht].SigmaPeakTime = mSigmaPeakTime;
5141  tjs.fHits[mht].PeakAmplitude = mChg / (2.5066 * mRMS);
5142  tjs.fHits[mht].SigmaPeakAmp = mSigmaPeakAmp;
5143  tjs.fHits[mht].Integral = mChg;
5144  tjs.fHits[mht].SigmaIntegral = mSigmaIntegral;
5145  tjs.fHits[mht].RMS = mRMS;
5146  tjs.fHits[mht].Multiplicity = 1;
5147  tjs.fHits[mht].LocalIndex = 0;
5148  tjs.fHits[mht].GoodnessOfFit = 1; // flag?
5149  tjs.fHits[mht].NDOF = 0;
5150  // then flag the other hits for erasing
5151  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
5152  for(unsigned short jj = 1; jj < oldHits.size(); ++jj) {
5153  if (tp.Hits[ii]==oldHits[jj]){
5154  tp.UseHit[ii] = false;
5155  // put it in the removal list
5156  delHits.push_back(tp.Hits[ii]);
5157  // Flag this hit
5158  tjs.fHits[tp.Hits[ii]].InTraj = SHRT_MAX;
5159  tp.Hits[ii] = INT_MAX;
5160  }
5161  }
5162  } // ii
5163  } // ipt
5164  } // itj
5165 
5166  // Erase the hits. Start by sorting them in decreasing order so that
5167  // the local delHits vector doesn't need to be modified when a hit is deleted
5168  if(delHits.size() > 1) std::sort(delHits.begin(), delHits.end(), std::greater<unsigned int>());
5169 
5170  for(auto& delHit : delHits) EraseHit(delHit);
5171 
5172  } // MergeTPHits
5173 
5175  void TrajClusterAlg::MaskBadTPs(Trajectory& tj, float const& maxChi)
5176  {
5177  // Remove TPs that have the worst values of delta until the fit chisq < maxChi
5178 
5179  if(!tjs.UseAlg[kMaskBadTPs]) return;
5180  //don't use this function for reverse propagation
5181  if(!tjs.UseAlg[kRvPrp]) return;
5182 
5183  if(tj.Pts.size() < 3) {
5184  mf::LogError("TC")<<"MaskBadTPs: Trajectory ID "<<tj.ID<<" too short to mask hits ";
5185  fGoodTraj = false;
5186  return;
5187  }
5188  unsigned short nit = 0;
5189  TrajPoint& lastTP = tj.Pts[tj.Pts.size() - 1];
5190  while(lastTP.FitChi > maxChi && nit < 3) {
5191  float maxDelta = 0;
5192  unsigned short imBad = USHRT_MAX;
5193  unsigned short cnt = 0;
5194  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
5195  unsigned short ipt = tj.Pts.size() - 1 - ii;
5196  TrajPoint& tp = tj.Pts[ipt];
5197  if(tp.Chg == 0) continue;
5198  if(tp.Delta > maxDelta) {
5199  maxDelta = tp.Delta;
5200  imBad = ipt;
5201  }
5202  ++cnt;
5203  if(cnt == tp.NTPsFit) break;
5204  } // ii
5205  if(imBad == USHRT_MAX) return;
5206  if(prt) mf::LogVerbatim("TC")<<"MaskBadTPs: lastTP.FitChi "<<lastTP.FitChi<<" Mask point "<<imBad;
5207  // mask the point
5208  UnsetUsedHits(tjs, tj.Pts[imBad]);
5209  FitTraj(tjs, tj);
5210  if(prt) mf::LogVerbatim("TC")<<" after FitTraj "<<lastTP.FitChi;
5211  tj.AlgMod[kMaskBadTPs] = true;
5212  ++nit;
5213  } // lastTP.FItChi > maxChi && nit < 3
5214 
5215  } // MaskBadTPs
5216 
5218  void TrajClusterAlg::MaskTrajEndPoints(Trajectory& tj, unsigned short nPts)
5219  {
5220  //PrintTrajectory("MTEP", tjs, tj, USHRT_MAX);
5221 
5222  // Masks off (sets all hits not-Used) nPts trajectory points at the leading edge of the
5223  // trajectory, presumably because the fit including this points is poor. The position, direction
5224  // and Delta of the last nPts points is updated as well
5225 
5226  if(tj.Pts.size() < 3) {
5227  mf::LogError("TC")<<"MaskTrajEndPoints: Trajectory ID "<<tj.ID<<" too short to mask hits ";
5228  fGoodTraj = false;
5229  return;
5230  }
5231  if(nPts > tj.Pts.size() - 2) {
5232  mf::LogError("TC")<<"MaskTrajEndPoints: Trying to mask too many points "<<nPts<<" Pts.size "<<tj.Pts.size();
5233  fGoodTraj = false;
5234  return;
5235  }
5236 
5237  // find the last good point (with charge)
5238  unsigned short lastGoodPt = USHRT_MAX ;
5239 
5240  if (!ChkMichel(tj, lastGoodPt)){ //did not find michel electron
5241  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
5242  unsigned short ipt = tj.EndPt[1] - nPts - ii;
5243  if(tj.Pts[ipt].Chg > 0) {
5244  lastGoodPt = ipt;
5245  break;
5246  }
5247  if(ipt == 0) break;
5248  } // ii
5249  }
5250  if(prt) {
5251  mf::LogVerbatim("TC")<<"MTEP: lastGoodPt "<<lastGoodPt<<" Pts size "<<tj.Pts.size()<<" fGoodTraj "<<fGoodTraj;
5252  }
5253  if(lastGoodPt == USHRT_MAX) return;
5254  tj.EndPt[1] = lastGoodPt;
5255 
5256  //for(unsigned short ii = 0; ii < nPts; ++ii) {
5257  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
5258  unsigned short ipt = tj.Pts.size() - 1 - ii;
5259  if (ipt==lastGoodPt) break;
5260  UnsetUsedHits(tjs, tj.Pts[ipt]);
5261  // Reset the position and direction of the masked off points
5262  tj.Pts[ipt].Dir = tj.Pts[lastGoodPt].Dir;
5263  if(tj.Pts[lastGoodPt].AngleCode == 2) {
5264  // Very large angle: Move by path length
5265  float path = TrajPointSeparation(tj.Pts[lastGoodPt], tj.Pts[ipt]);
5266  tj.Pts[ipt].Pos[0] = tj.Pts[lastGoodPt].Pos[0] + path * tj.Pts[ipt].Dir[0];
5267  tj.Pts[ipt].Pos[1] = tj.Pts[lastGoodPt].Pos[1] + path * tj.Pts[ipt].Dir[1];
5268  } else {
5269  // Not large angle: Move by wire
5270  float dw = tj.Pts[ipt].Pos[0] - tj.Pts[lastGoodPt].Pos[0];
5271  // Correct the projected time to the wire
5272  float newpos = tj.Pts[lastGoodPt].Pos[1] + dw * tj.Pts[ipt].Dir[1] / tj.Pts[ipt].Dir[0];
5273  if(prt) mf::LogVerbatim("TC")<<"MTEP: ipt "<<ipt<<" Pos[0] "<<tj.Pts[ipt].Pos[0]<<". Move Pos[1] from "<<tj.Pts[ipt].Pos[1]<<" to "<<newpos;
5274  tj.Pts[ipt].Pos[1] = tj.Pts[lastGoodPt].Pos[1] + dw * tj.Pts[ipt].Dir[1] / tj.Pts[ipt].Dir[0];
5275  }
5276  tj.Pts[ipt].Delta = PointTrajDOCA(tjs, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tj.Pts[ipt]);
5277  if(prt) mf::LogVerbatim("TC")<<" masked ipt "<<ipt<<" Pos "<<PrintPos(tjs, tj.Pts[ipt])<<" Chg "<<tj.Pts[ipt].Chg;
5278  } // ii
5279  SetEndPoints(tjs, tj);
5280 
5281  } // MaskTrajEndPoints
5282 
5285  {
5286  // Sets the StopFlag[kBragg] bits on the trajectory by identifying the Bragg peak
5287  // at each end. This function checks both ends, finding the point with the highest charge nearest the
5288  // end and considering the first (when end = 0) 4 points or last 4 points (when end = 1). The next
5289  // 5 - 10 points (fChkStop[0]) are fitted to a line, Q(x - x0) = Qo + (x - x0) * slope where x0 is the
5290  // wire position of the highest charge point. A large negative slope indicates that there is a Bragg
5291  // peak at the end.
5292 
5293  tj.StopFlag[0][kBragg] = false;
5294  tj.StopFlag[1][kBragg] = false;
5295  if(!tjs.UseAlg[kChkStop]) return;
5296  if(fChkStopCuts[0] < 0) return;
5297 
5298  // don't attempt with low momentum trajectories
5299  if(tj.MCSMom < 30) return;
5300 
5301  // ignore trajectories that are very large angle at both ends
5302  if(tj.Pts[tj.EndPt[0]].AngleCode == 2 || tj.Pts[tj.EndPt[1]].AngleCode == 2) return;
5303 
5304  unsigned short nPtsToCheck = fChkStopCuts[1];
5305  if(tj.Pts.size() < nPtsToCheck) return;
5306 
5307  if(prt) mf::LogVerbatim("TC")<<"ChkStop: requiring "<<nPtsToCheck<<" points with charge slope > "<<fChkStopCuts[0]<<" Chg/WSEU";
5308 
5309  // find the highest charge hit in the first 3 points at each end
5310  for(unsigned short end = 0; end < 2; ++end) {
5311  short dir = 1 - 2 * end;
5312  // find the point with the highest charge considering the first 3 points
5313  float big = 0;
5314  unsigned short hiPt = 0;
5315  float wire0 = 0;
5316  for(unsigned short ii = 0; ii < 4; ++ii) {
5317  short ipt = tj.EndPt[end] + ii * dir;
5318  if(ipt < tj.EndPt[0] || ipt > tj.EndPt[1]) break;
5319  TrajPoint& tp = tj.Pts[ipt];
5320  if(tp.Chg > big) {
5321  big = tp.Chg;
5322  wire0 = tp.Pos[0];
5323  hiPt = ipt;
5324  }
5325  } // ii
5326  if(prt) mf::LogVerbatim("TC")<<" end "<<end<<" wire0 "<<wire0<<" Chg "<<big;
5327  std::vector<float> x, y, yerr2;
5328  float intcpt, intcpterr;
5329  float slope, slopeerr, chidof;
5330  float prevChg = big;
5331  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
5332  short ipt = hiPt + ii * dir;
5333  if(ipt < tj.EndPt[0] || ipt > tj.EndPt[1]) break;
5334  TrajPoint& tp = tj.Pts[ipt];
5335  if(tp.Chg == 0) continue;
5336  // quit if the charge is much larger than the previous charge
5337  if(tp.Chg > 1.5 * prevChg) break;
5338  prevChg = tp.Chg;
5339  x.push_back(std::abs(tp.Pos[0] - wire0));
5340  y.push_back(tp.Chg);
5341  // Assume 10% point-to-point charge fluctuations
5342  float err = 0.1 * tp.Chg;
5343  if(prt) mf::LogVerbatim("TC")<<ipt<<" "<<PrintPos(tjs, tp.Pos)<<" "<<x[x.size()-1]<<" Chg "<<(int)tp.Chg;
5344  yerr2.push_back(err * err);
5345  if(x.size() == nPtsToCheck) break;
5346  } // ii
5347  if(x.size() < 4) continue;
5348  fLinFitAlg.LinFit(x, y, yerr2, intcpt, slope, intcpterr, slopeerr, chidof);
5349  // check for really bad chidof indicating a major failure
5350  if(chidof > 100) continue;
5351  unsigned short endPt = tj.EndPt[end];
5352  if(tj.Pts[endPt].AveChg > 0 && intcpt / tj.Pts[endPt].AveChg > 3) {
5353 // std::cout<<"ChkStop: Crazy intcpt "<<intcpt<<" "<<tj.Pts[endPt].AveChg<<"\n";
5354  continue;
5355  }
5356  // The charge slope is negative for a stopping track in the way that the fit was constructed.
5357  // Flip the sign so we can make a cut against fChkStopCuts[0] which is positive.
5358  slope = -slope;
5359  if(slope > fChkStopCuts[0] && chidof < fChkStopCuts[2] && slope > 2 * slopeerr) {
5360  tj.StopFlag[end][kBragg] = true;
5361  tj.AlgMod[kChkStop] = true;
5362  // Put the charge at the end into tp.AveChg
5363  tj.Pts[endPt].AveChg = intcpt;
5364  // see if we can tag it as a proton
5365  std::vector<int> tjlist(1, tj.ID);
5366  float chgFrac = ChgFracNearPos(tjs, tj.Pts[endPt].Pos, tjlist);
5367  if(chgFrac > 0.9) tj.PDGCode = 2212;
5368  if(prt) mf::LogVerbatim("TC")<<" end "<<end<<" fit chidof "<<chidof<<" slope "<<slope<<" +/- "<<slopeerr<<" proton tag "<<tj.PDGCode;
5369  } else {
5370  if(prt) mf::LogVerbatim("TC")<<" end "<<end<<" fit chidof "<<chidof<<" slope "<<slope<<" +/- "<<slopeerr<<" Not stopping";
5371  }
5372  } // end
5373 
5374  } // ChkStop
5375 
5377  bool TrajClusterAlg::ChkMichel(Trajectory& tj, unsigned short& lastGoodPt){
5378 
5379  if(!tjs.UseAlg[kMichel]) return false;
5380  //find number of hits that are consistent with Michel electron
5381  unsigned short nmichelhits = 0;
5382  //find number of hits that are consistent with Bragg peak
5383  unsigned short nbragghits = 0;
5384  float lastChg = 0;
5385 
5386  bool isfirsthit = true;
5387  unsigned short braggpeak = 0;
5388 
5389  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
5390  if (ii>tj.EndPt[1]) continue;
5391  unsigned short ipt = tj.EndPt[1] - ii;
5392  if (tj.Pts[ipt].Chg>0){
5393  if (isfirsthit){
5394  isfirsthit = false;
5395  if (tj.Pts[ipt].ChgPull<0){
5396  ++nmichelhits;
5397  }
5398  }
5399  else{
5400  if (tj.Pts[ipt].ChgPull<0&&nmichelhits&&!nbragghits){//still Michel
5401  ++nmichelhits;
5402  }
5403  else{
5404  if (!nbragghits){
5405  ++nbragghits; //Last Bragg peak hit
5406  lastChg = tj.Pts[ipt].Chg;
5407  braggpeak = ipt;
5408  }
5409  else if (tj.Pts[ipt].Chg<lastChg){ //still Bragg peak
5410  ++nbragghits;
5411  lastChg = tj.Pts[ipt].Chg;
5412  }
5413  else break;
5414  }
5415  }
5416  }
5417  }
5418  if(prt) mf::LogVerbatim("TC")<<"ChkMichel Michel hits: "<<nmichelhits<<" Bragg peak hits: "<<nbragghits;
5419  if (nmichelhits>0&&nbragghits>2){//find Michel topology
5420  lastGoodPt = braggpeak;
5421  tj.AlgMod[kMichel] = true;
5422  return true;
5423  }
5424  else{
5425  return false;
5426  }
5427  }
5428 
5431  {
5432  // Check allTraj trajectories in the current CTP to see if they are stopping
5433  if(!tjs.UseAlg[kSplitHiChgHits]) return;
5434 
5435  for(size_t i = 0; i< tjs.allTraj.size(); ++i) {
5436  auto & tj = tjs.allTraj[i];
5437  if(tj.CTP != inCTP) continue;
5438  if(tj.AlgMod[kKilled]) continue;
5439  SplitHiChgHits(tj);
5440  } // tj
5441 
5442  } // ChkHiChgHits
5443 
5446 
5447  // Check allTraj trajectories in the current CTP and split high charge hits
5448  if(!tjs.UseAlg[kSplitHiChgHits]) return;
5449 
5450  // Only do it once
5451  if (tj.AlgMod[kSplitHiChgHits]) return;
5452 
5453  if(tj.AlgMod[kKilled]) return;
5454  //Ignore short trajectories
5455  if (tj.EndPt[1]<10) return;
5456  for(unsigned short end = 0; end < 2; ++end) {
5457  if(prt) mf::LogVerbatim("TC")<<"SplitHiChghits "<<end<<" "<<tj.VtxID[end];
5458  float hichg = 0;
5459  unsigned short tp = tj.EndPt[end];
5460  unsigned short nlohits = 0;
5461  unsigned short lastHiTP = USHRT_MAX;
5462  while (tp != tj.EndPt[1-end]){
5463  float ptchg = TpSumHitChg(tjs, tj.Pts[tp]);
5464  if (prt) mf::LogVerbatim("TC")<<"SplitHiChgHits "<<tp<<" "<<ptchg<<" "<<PrintPos(tjs, tj.Pts[tp]);
5465  if (ptchg){
5466  if (tp == tj.EndPt[end]){
5467  hichg = ptchg;
5468  lastHiTP = tp;
5469  }
5470  else if (ptchg>0.4*hichg){
5471  if (!nlohits){
5472  hichg = ptchg;
5473  lastHiTP = tp;
5474  }
5475  else{
5476  break;
5477  }
5478  }
5479  else ++nlohits;
5480  }
5481  if (end==0){
5482  ++tp;
5483  }
5484  else{
5485  --tp;
5486  }
5487  }
5488  //if (prt) mf::LogVerbatim("TC")<<"SplitHiChgHits "<<end<<" "<<nlohits;
5489  if (nlohits>4&&lastHiTP!=USHRT_MAX){
5490  //Create new vertex
5491  VtxStore aVtx;
5492  aVtx.Pos = tj.Pts[lastHiTP].Pos;
5493  aVtx.NTraj = 2;
5494  aVtx.Pass = tj.Pass;
5495  aVtx.Topo = 7;
5496  aVtx.ChiDOF = 0;
5497  aVtx.CTP = tj.CTP;
5498  aVtx.ID = tjs.vtx.size() + 1;
5499  if(!StoreVertex(tjs, aVtx)) {
5500  if(prt) mf::LogVerbatim("TC")<<" Failed storing vertex "<<tj.VtxID[end];
5501  return;
5502  }
5503 
5504  // make a copy
5505  Trajectory newTj = tj;
5506  newTj.ID = tjs.allTraj.size() + 1;
5507 
5508  // keep high charge hits, reassign other hits to the new trajectory
5509  unsigned short tp1 = lastHiTP+1;
5510  if (end==1) tp1 = lastHiTP-1;
5511  for (unsigned short ipt = std::min(tj.EndPt[1-end], tp1); ipt <= std::max(tj.EndPt[1-end], tp1); ++ipt){
5512  tj.Pts[ipt].Chg = 0;
5513  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
5514  if(!tj.Pts[ipt].UseHit[ii]) continue;
5515  unsigned int iht = tj.Pts[ipt].Hits[ii];
5516  // This shouldn't happen but check anyway
5517  if(tjs.fHits[iht].InTraj != tj.ID) continue;
5518  tjs.fHits[iht].InTraj = newTj.ID;
5519  tj.Pts[ipt].UseHit[ii] = false;
5520  }//ii
5521  }//ipt
5522  SetEndPoints(tjs, tj);
5523  tj.VtxID[1-end] = aVtx.ID;
5524  tj.AlgMod[kSplitHiChgHits] = true;
5525  if(prt) {
5526  mf::LogVerbatim("TC")<<"Splitting trajectory ID "<<tj.ID<<" new EndPts "<<tj.EndPt[0]<<" to "<<tj.EndPt[1];
5527  }
5528 
5529  for (unsigned short ipt = std::min(newTj.EndPt[end], lastHiTP); ipt <= std::max(newTj.EndPt[end], lastHiTP); ++ipt){
5530  newTj.Pts[ipt].Chg = 0;
5531  for (unsigned short ii = 0; ii < newTj.Pts[ipt].Hits.size(); ++ii) {
5532  newTj.Pts[ipt].UseHit[ii] = false;
5533  }//ii
5534  }//ipt
5535  SetEndPoints(tjs, newTj);
5536  newTj.VtxID[end] = aVtx.ID;
5537  newTj.AlgMod[kSplitHiChgHits] = true;
5538  tjs.allTraj.push_back(newTj);
5539  SetVx2Score(tjs, prt);
5540 
5541  break;
5542  }
5543  }
5544  }
5545 
5548  showertree = t;
5549 
5550  showertree->Branch("run", &tjs.Run, "run/I");
5551  showertree->Branch("subrun", &tjs.SubRun, "subrun/I");
5552  showertree->Branch("event", &tjs.Event, "event/I");
5553 
5554  showertree->Branch("BeginWir", &tjs.stv.BeginWir);
5555  showertree->Branch("BeginTim", &tjs.stv.BeginTim);
5556  showertree->Branch("BeginAng", &tjs.stv.BeginAng);
5557  showertree->Branch("BeginChg", &tjs.stv.BeginChg);
5558  showertree->Branch("BeginVtx", &tjs.stv.BeginVtx);
5559 
5560  showertree->Branch("EndWir", &tjs.stv.EndWir);
5561  showertree->Branch("EndTim", &tjs.stv.EndTim);
5562  showertree->Branch("EndAng", &tjs.stv.EndAng);
5563  showertree->Branch("EndChg", &tjs.stv.EndChg);
5564  showertree->Branch("EndVtx", &tjs.stv.EndVtx);
5565 
5566  showertree->Branch("MCSMom", &tjs.stv.MCSMom);
5567 
5568  showertree->Branch("PlaneNum", &tjs.stv.PlaneNum);
5569  showertree->Branch("TjID", &tjs.stv.TjID);
5570  showertree->Branch("IsShowerTj", &tjs.stv.IsShowerTj);
5571  showertree->Branch("ShowerID", &tjs.stv.ShowerID);
5572  showertree->Branch("IsShowerParent", &tjs.stv.IsShowerParent);
5573  showertree->Branch("StageNum", &tjs.stv.StageNum);
5574  showertree->Branch("StageName", &tjs.stv.StageName);
5575 
5576  showertree->Branch("Envelope", &tjs.stv.Envelope);
5577  showertree->Branch("EnvPlane", &tjs.stv.EnvPlane);
5578  showertree->Branch("EnvStage", &tjs.stv.EnvStage);
5579  showertree->Branch("EnvShowerID", &tjs.stv.EnvShowerID);
5580 
5581  showertree->Branch("nStages", &tjs.stv.nStages);
5582  showertree->Branch("nPlanes", &tjs.stv.nPlanes);
5583 
5584  } // end DefineShTree
5585 
5588  crtree = t;
5589  crtree->Branch("run", &tjs.Run, "run/I");
5590  crtree->Branch("subrun", &tjs.SubRun, "subrun/I");
5591  crtree->Branch("event", &tjs.Event, "event/I");
5592  crtree->Branch("cr_origin", &tjs.crt.cr_origin);
5593  crtree->Branch("cr_pfpxmin", &tjs.crt.cr_pfpxmin);
5594  crtree->Branch("cr_pfpxmax", &tjs.crt.cr_pfpxmax);
5595  crtree->Branch("cr_pfpyzmindis", &tjs.crt.cr_pfpyzmindis);
5596  }
5597 
5600  {
5601  // Puts the hits and MCParticles into TjStuff
5602  tjs.fHits.clear();
5603  tjs.MCPartList.clear();
5604 
5605  art::ValidHandle< std::vector<recob::Hit>> hitVecHandle = evt.getValidHandle<std::vector<recob::Hit>>(fHitFinderModuleLabel);
5606  if(hitVecHandle->size() < 2) return;
5607  tjs.fHits.reserve(hitVecHandle->size());
5608 
5609  for(unsigned int iht = 0; iht < hitVecHandle->size(); ++iht) {
5610  art::Ptr<recob::Hit> hit = art::Ptr<recob::Hit>(hitVecHandle, iht);
5611  TCHit localHit;
5612  localHit.StartTick = hit->StartTick();
5613  localHit.EndTick = hit->EndTick();
5614  localHit.PeakTime = hit->PeakTime();
5615  localHit.SigmaPeakTime = hit->SigmaPeakTime();
5616  localHit.PeakAmplitude = hit->PeakAmplitude();
5617  localHit.SigmaPeakAmp = hit->SigmaPeakAmplitude();
5618  localHit.Integral = hit->Integral();
5619  localHit.SigmaIntegral = hit->SigmaIntegral();
5620  localHit.RMS = hit->RMS();
5621  localHit.GoodnessOfFit = hit->GoodnessOfFit();
5622  localHit.NDOF = hit->DegreesOfFreedom();
5623  localHit.Multiplicity = hit->Multiplicity();
5624  localHit.LocalIndex = hit->LocalIndex();
5625  localHit.ArtPtr = hit;
5626  tjs.fHits.push_back(localHit);
5627  } // iht
5628 
5629  // sort it as needed;
5630  // that is, sorted by wire ID number,
5631  // then by start of the region of interest in time, then by the multiplet
5632  std::sort(tjs.fHits.begin(), tjs.fHits.end(), &SortByMultiplet);
5633 
5634  // check the hits for local index errors
5635  unsigned short nerr = 0;
5636  for(unsigned int iht = 0; iht < tjs.fHits.size(); ++iht) {
5637  if(tjs.fHits[iht].Multiplicity < 2) continue;
5638  auto& iHit = tjs.fHits[iht];
5639  bool badHit = false;
5640  unsigned int fromIndex = iht - iHit.LocalIndex;
5641  unsigned short indx = 0;
5642  for(unsigned int jht = fromIndex; jht < fromIndex + iHit.Multiplicity; ++jht) {
5643  auto& jHit = tjs.fHits[jht];
5644  if(iHit.Multiplicity != jHit.Multiplicity) badHit = true;
5645  if(iHit.LocalIndex != indx) badHit = true;
5646  ++indx;
5647  } // jht
5648  if(badHit) {
5649  ++nerr;
5650  for(unsigned int jht = fromIndex; jht < fromIndex + iHit.Multiplicity; ++jht) {
5651  auto& jHit = tjs.fHits[jht];
5652  jHit.Multiplicity = 1;
5653  jHit.LocalIndex = 0;
5654  } // jht
5655  }
5656  } // iht
5657 
5658  if(evt.isRealData()) return;
5659  // don't bother loading MC info if it won't be used.
5660  if(tjs.MatchTruth[0] < 0) return;
5661 
5662  // save MCParticles in TjStuff that have the desired MCTruth origin using
5663  // the Origin_t typedef enum: kUnknown, kBeamNeutrino, kCosmicRay, kSuperNovaNeutrino, kSingleParticle
5665  // or save them all
5666  bool anySource = (origin == simb::kUnknown);
5667 
5668  if(tjs.MatchTruth[1] > 0) {
5669  // print MCTruth ala the event display
5670  std::vector<art::Handle<std::vector<simb::MCTruth>>> mctcol;
5671  evt.getManyByType(mctcol);
5672  std::vector<const simb::MCTruth*> mcvec;
5673  for(size_t mctc = 0; mctc < mctcol.size(); ++mctc) {
5674  art::Handle< std::vector<simb::MCTruth> > mclistHandle = mctcol[mctc];
5675  for(size_t i = 0; i < mclistHandle->size(); ++i){
5676  mcvec.push_back(&(mclistHandle->at(i)));
5677  }
5678  }
5679  std::cout<<"MCtruth Part TrkID PDGCode KE Mother Process\n";
5680  for(unsigned int i = 0; i < mcvec.size(); ++i) {
5681  if(!anySource && mcvec[i]->Origin() != origin) continue;
5682 // std::cout<<"mcvec "<<i<<" Origin "<<mcvec[i]->Origin()<<" NParticles "<<mcvec[i]->NParticles()<<"\n";
5683  for(int ii = 0; ii < mcvec[i]->NParticles(); ++ii) {
5684  auto& p = mcvec[i]->GetParticle(ii);
5685  if(!(p.StatusCode() == 0 || p.StatusCode() == 1)) continue;
5686  int KE = 1000 * (p.E() - p.Mass());
5687  std::cout<<std::setw(6)<<i<<std::setw(6)<<ii;
5688  std::cout<<std::setw(6)<<p.TrackId();
5689  std::cout<<std::setw(12)<<p.PdgCode();
5690  std::cout<<std::setw(7)<<KE;
5691  std::cout<<std::setw(6)<<p.Mother();
5692  std::cout<<" "<<p.Process();
5693 // std::cout<<" "<<p.StatusCode();
5694  std::cout<<"\n";
5695  } // ii
5696  } // i
5697  }
5698 
5700  sim::ParticleList const& plist = pi_serv->ParticleList();
5701  if(plist.empty()) return;
5702 // std::cout<<" ParticleInventoryService ParticleList size "<<plist.size()<<"\n";
5703  for(sim::ParticleList::const_iterator ipart = plist.begin(); ipart != plist.end(); ++ipart) {
5704  auto& p = (*ipart).second;
5705  int trackID = p->TrackId();
5706  art::Ptr<simb::MCTruth> theTruth = pi_serv->TrackIdToMCTruth_P(trackID);
5707  int KE = 1000 * (p->E() - p->Mass());
5708  if(!anySource && theTruth->Origin() != origin) continue;
5709  if(tjs.MatchTruth[1] > 1 && KE > 10) {
5710  std::cout<<"GHC: mcp Origin "<<theTruth->Origin()
5711  <<std::setw(8)<<p->TrackId()
5712  <<" pdg "<<p->PdgCode()
5713  <<std::setw(7)<<KE<<" mom "<<p->Mother()
5714  <<" "<<p->Process()
5715  <<"\n";
5716  }
5717  tjs.MCPartList.push_back(p);
5718  } // ipart
5719 
5720  if(fUseOldBackTracker) {
5721  tm.MatchTrueHits();
5722  return;
5723  }
5724 
5725 // std::cout<<"Hit ProductID "<<tjs.fHits[0].ArtPtr.id()<<"\n";
5726 
5728  std::vector<art::Ptr<simb::MCParticle>> particle_vec;
5729  std::vector<anab::BackTrackerHitMatchingData const*> match_vec;
5730  unsigned int nMatHits = 0;
5731  // prthit is a temporary debugging variable
5732  bool prthit = false;
5733  // associate a hit with a MCParticle > 50% of the deposited energy is from it
5734  for(unsigned int iht = 0; iht < tjs.fHits.size(); ++iht) {
5735  particle_vec.clear(); match_vec.clear();
5736  if(prthit) {
5737  std::cout<<"Hit "<<PrintHit(tjs.fHits[iht])<<" key "<<tjs.fHits[iht].ArtPtr.key()<<" StartTick "<<tjs.fHits[iht].StartTick<<"\n";
5738  }
5739  try{ particles_per_hit.get(tjs.fHits[iht].ArtPtr.key(), particle_vec, match_vec); }
5740  catch(...) {
5741  std::cout<<"BackTrackerHitMatchingData not found using "<<fHitTruthModuleLabel<<". Try to use the old backtracker\n";
5742  fUseOldBackTracker = true;
5743  tm.MatchTrueHits();
5744  return;
5745  }
5746  if(particle_vec.empty()) continue;
5747  int trackID = 0;
5748  for(unsigned short im = 0; im < match_vec.size(); ++im) {
5749  if(prthit) std::cout<<" im "<<im<<" trackID "<<particle_vec[im]->TrackId()<<" ideFraction "<<match_vec[im]->ideFraction<<"\n";
5750  if(match_vec[im]->ideFraction > 0.5) {
5751  trackID = particle_vec[im]->TrackId();
5752  break;
5753  } // ideFraction > 0.5
5754  } // im
5755  if(trackID == 0) continue;
5756  if(prthit) {
5757  for(sim::ParticleList::const_iterator ipart = plist.begin(); ipart != plist.end(); ++ipart) {
5758  auto& p = (*ipart).second;
5759  if(p->TrackId() != trackID) continue;
5760  art::Ptr<simb::MCTruth> theTruth = pi_serv->TrackIdToMCTruth_P(trackID);
5761  std::cout<<"hit "<<PrintHit(tjs.fHits[iht])<<" -> trackID "<<trackID<<" Origin "<<theTruth->Origin()<<" pdg "<<p->PdgCode()<<" E "<<1000 * p->E()<<" Process "<<p->Process()<<"\n";
5762  }
5763  }
5764  // look for this in MCPartList
5765  for(unsigned int ipart = 0; ipart < tjs.MCPartList.size(); ++ipart) {
5766  auto& mcp = tjs.MCPartList[ipart];
5767  if(mcp->TrackId() != trackID) continue;
5768  tjs.fHits[iht].MCPartListIndex = ipart;
5769  ++nMatHits;
5770  break;
5771  } // ipart
5772  } // iht
5773 
5774  // Optionally set InTraj to a bogus ID for special studies, for instance to speed up
5775  // reconstruction of events that have many background hits from processes that we aren't
5776  // interested in, for instance cosmic hits that weren't properly removed.
5777  if(tjs.MatchTruth.size() > 4 && tjs.MatchTruth[4] > 0) {
5778  std::cout<<"MatchTrueHits: Ignoring hits not matched to MC particles\n";
5779  for(auto& hit : tjs.fHits) {
5780  if(hit.MCPartListIndex == UINT_MAX) hit.InTraj = INT_MAX;
5781  } // hit
5782  }
5783 
5784  if(tjs.MatchTruth[1] > 0) {
5785  std::cout<<"GetHitCollection loaded "<<tjs.MCPartList.size()<<" MCParticles using MCTruth Origin "<<origin;
5786  std::cout<<". Matched "<<nMatHits<<" hits to MCParticles out of "<<tjs.fHits.size()<<" total hits";
5787  std::cout<<"\n";
5788  }
5789 
5790  } // GetHitCollection
5791 } // 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
bool fMakeNewHits
label of module producing input hits
std::bitset< 16 > UseHit
Definition: DataStructs.h:163
void ClearResults()
Deletes all the results.
bool valDecreasing(SortEntry c1, SortEntry c2)
Definition: TCVertex.cxx:10
don&#39;t mess with this line
Definition: DataStructs.h:450
float AveChg
Calculated using ALL hits.
Definition: DataStructs.h:174
std::vector< int > EnvStage
Definition: DataStructs.h:358
void UpdateDeltaRMS(Trajectory &tj)
label of module producing input hits
Point2_t Pos
Definition: DataStructs.h:147
std::string PrintPos(const TjStuff &tjs, const TrajPoint &tp)
Definition: Utils.cxx:4738
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< unsigned int > FindCloseHits(TjStuff const &tjs, std::array< int, 2 > const &wireWindow, Point2_t const &timeWindow, const unsigned short plane, HitStatus_t hitRequest, bool usePeakTime, bool &hitsNear)
Definition: Utils.cxx:2362
std::vector< int > IsShowerParent
Definition: DataStructs.h:351
bool ChkVtxAssociations(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:2128
SubRunNumber_t subRun() const
Definition: Event.h:72
float fMaxChi
label of module producing input hits
bool SelectEvent
number of points to find AveChg
Definition: DataStructs.h:533
std::vector< float > cr_pfpyzmindis
Definition: DataStructs.h:370
float fHitErrFac
hit time error = fHitErrFac * hit RMS used for cluster fit
bool empty() const
Definition: ParticleList.h:314
void ChkInTraj(std::string someText)
label of module producing input hits
std::vector< unsigned int > tclhits
Definition: DataStructs.h:79
void MergeTPHits()
label of module producing input hits
std::vector< float > Vertex2DCuts
Max position pull, max Position error rms.
Definition: DataStructs.h:509
void ChkChgAsymmetry(TjStuff &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:1436
void ChkStopEndPts(Trajectory &tj, bool prt)
label of module producing input hits
std::vector< float > ChargeCuts
Definition: DataStructs.h:519
std::vector< float > EndWir
Definition: DataStructs.h:338
float fMaxWireSkipNoSignal
max number of wires to skip w/o a signal on them
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:83
void PrintTrajectory(std::string someText, const TjStuff &tjs, const Trajectory &tj, unsigned short tPoint)
Definition: Utils.cxx:4488
FindVtxTraj algorithm tried.
Definition: DataStructs.h:101
std::vector< float > EndAng
Definition: DataStructs.h:340
float OverlapFraction(TjStuff &tjs, const Trajectory &tj1, const Trajectory &tj2)
Definition: Utils.cxx:584
const std::vector< std::string > AlgBitNames
Definition: DataStructs.cxx:4
void Print2DShowers(std::string someText, const TjStuff &tjs, CTP_t inCTP, bool printKilledShowers)
Definition: TCShower.cxx:4713
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:145
std::vector< unsigned short > fMinPtsFit
StepCrawl mode (0 = turn off)
unsigned short NTPsFit
Definition: DataStructs.h:159
void ChkStop(Trajectory &tj)
label of module producing input hits
float HitSep2(TjStuff &tjs, unsigned int iht, unsigned int jht)
Definition: Utils.cxx:2110
art::Ptr< recob::Hit > ArtPtr
Definition: DataStructs.h:206
bool ChkMichel(Trajectory &tj, unsigned short &lastGoodPt)
label of module producing input hits
void PrepareForNextPass(Trajectory &tj)
label of module producing input hits
unsigned short PDGCode
Definition: DataStructs.h:68
std::vector< float > cr_pfpxmax
Definition: DataStructs.h:369
void ChkHiChgHits(CTP_t inCTP)
label of module producing input hits
TjStuff tjs
label of module producing input hits
void Find3DVertices(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:1215
std::bitset< 64 > AlgMod
Bit set if algorithm AlgBit_t modifed the trajectory.
Definition: DataStructs.h:171
unsigned int index
Definition: TCShower.cxx:5
void UpdateVxEnvironment(std::string inFcnLabel, TjStuff &tjs, VtxStore &vx2, bool prt)
Definition: Utils.cxx:3297
const key_type & TrackId(const size_type) const
void MakeAllTrajClusters()
label of module producing input hits
std::vector< float > BeginTim
Definition: DataStructs.h:334
unsigned short AngleRange(TjStuff &tjs, TrajPoint const &tp)
Definition: Utils.cxx:636
void FindVtxTraj(VtxStore &theVtx)
label of module producing input hits
void PrintAllTraj(std::string someText, const TjStuff &tjs, const DebugStuff &debug, unsigned short itj, unsigned short ipt, bool prtVtx)
Definition: Utils.cxx:4232
TruthMatcher tm
label of module producing input hits
bool HasDuplicateHits(TjStuff const &tjs, Trajectory const &tj, bool prt)
Definition: Utils.cxx:2335
unsigned short Step
Definition: DataStructs.h:160
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
std::vector< unsigned int > PutTrajHitsInVector(Trajectory const &tj, HitStatus_t hitRequest)
Definition: Utils.cxx:2283
void TagJunkTj(TjStuff const &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:2309
vertex position fixed manually - no fitting done
Definition: DataStructs.h:102
std::vector< float > AveHitRMS
average RMS of an isolated hit
Definition: DataStructs.h:483
std::vector< float > BeginAng
Definition: DataStructs.h:335
void SplitTrajCrossingVertices(TjStuff &tjs, CTP_t inCTP)
Definition: TCVertex.cxx:1091
Float_t y
Definition: compare.C:6
std::vector< float > MaxPos1
Definition: DataStructs.h:472
float TpSumHitChg(TjStuff &tjs, TrajPoint const &tp)
Definition: Utils.cxx:1678
std::array< std::bitset< 8 >, 2 > StopFlag
Definition: DataStructs.h:190
void FindJunkTraj(CTP_t inCTP)
label of module producing input hits
bool FitVertex(TjStuff &tjs, VtxStore &vx, bool prt)
Definition: TCVertex.cxx:1946
void DefineHitPos(TrajPoint &tp)
label of module producing input hits
list_type::const_iterator const_iterator
Definition: ParticleList.h:132
simb::Origin_t Origin() const
Definition: MCTruth.h:71
bool mrgPrt
label of module producing input hits
art::InputTag fHitFinderModuleLabel
label of module producing input hits
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
Geometry information for a single TPC.
Definition: TPCGeo.h:37
std::vector< PFPStruct > pfps
Definition: DataStructs.h:505
enum simb::_ev_origin Origin_t
event origin types
float fProjectionErrFactor
label of module producing input hits
int ParentID
ID of the parent, or the ID of the Tj this one was merged with if it is killed.
Definition: DataStructs.h:173
std::vector< unsigned int > Hits
Definition: DataStructs.h:162
void UpdateTraj(Trajectory &tj)
label of module producing input hits
unsigned int SubRun
Definition: DataStructs.h:523
void ClearShowerTree(ShowerTreeVars &stv)
Definition: TCShTree.cxx:203
CRTreeVars crt
Definition: DataStructs.h:489
float SigmaPeakAmplitude() const
Uncertainty on estimated amplitude of the hit at its peak, in ADC units.
Definition: Hit.h:223
void CreateHists(art::TFileService &tfs)
Definition: TCHist.cxx:5
float SigmaIntegral() const
Initial tdc tick for hit.
Definition: Hit.h:226
bool AttachAnyTrajToVertex(TjStuff &tjs, unsigned short ivx, bool prt)
Definition: TCVertex.cxx:1689
float HitsTimeErr2(const std::vector< unsigned int > &hitVec)
label of module producing input hits
unsigned short Pass
Definition: DataStructs.h:87
calo::CalorimetryAlg fCaloAlg
label of module producing input hits
void ConfigureMVA(TjStuff &tjs, std::string fMVAShowerParentWeights)
Definition: TCShower.cxx:15
unsigned int Run
Definition: DataStructs.h:522
bool TrajHitsOK(TjStuff &tjs, const std::vector< unsigned int > &iHitsInMultiplet, const std::vector< unsigned int > &jHitsInMultiplet)
Definition: Utils.cxx:1563
float HitsRMSTime(TjStuff &tjs, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:3607
int DegreesOfFreedom() const
Initial tdc tick for hit.
Definition: Hit.h:230
float HitsRMSTick(TjStuff &tjs, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:3613
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
ShowerTreeVars stv
Definition: DataStructs.h:485
void ChkVxTjs(TjStuff &tjs, const CTP_t &inCTP, bool prt)
Definition: TCVertex.cxx:715
void CheckHiMultUnusedHits(Trajectory &tj)
label of module producing input hits
void TrimEndPts(std::string fcnLabel, TjStuff &tjs, Trajectory &tj, const std::vector< float > &fQualityCuts, bool prt)
Definition: Utils.cxx:1342
float TrajPointSeparation(TrajPoint &tp1, TrajPoint &tp2)
Definition: Utils.cxx:2235
float Integral() const
Integral under the calibrated signal waveform of the hit, in tick x ADC units.
Definition: Hit.h:225
std::vector< float > fChkStopCuts
[Min Chg ratio, Chg slope pull cut, Chg fit chi cut]
float fMaxTrajSep
max trajectory point separation for making showers
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:313
std::vector< ShowerStruct3D > showers
Definition: DataStructs.h:508
void AddHits(Trajectory &tj, unsigned short ipt, bool &sigOK)
label of module producing input hits
Float_t tmp
Definition: plot.C:37
void SetAngleCode(TjStuff &tjs, TrajPoint &tp)
Definition: Utils.cxx:642
std::vector< unsigned short > fMinPts
min number of Pts required to make a trajectory
double DeltaAngle(const Vector3_t v1, const Vector3_t v2)
Definition: PFPUtils.cxx:1607
short fMode
label of module producing input hits
SigType_t SignalType(geo::PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
bool isRealData() const
Definition: Event.h:83
unsigned int ClusterIndex
Index not the ID...
Definition: DataStructs.h:186
unsigned int Event
Definition: DataStructs.h:524
unsigned short NumPlanes
Definition: DataStructs.h:475
float GoodnessOfFit() const
Degrees of freedom in the determination of the hit signal shape (-1 by default)
Definition: Hit.h:229
void GetHitMultiplet(unsigned int theHit, std::vector< unsigned int > &hitsInMultiplet)
label of module producing input hits
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
bool valDecreasing(SortEntry c1, SortEntry c2)
bool CompatibleMerge(TjStuff &tjs, std::vector< int > &tjIDs, bool prt)
Definition: Utils.cxx:455
std::array< double, 2 > Dir
Definition: DataStructs.h:148
void Initialize()
Definition: TCTruth.cxx:20
float fVLAStepSize
label of module producing input hits
void CheckTraj(Trajectory &tj)
label of module producing input hits
void FindSoftKink(Trajectory &tj)
label of module producing input hits
IteratorBox< TPC_id_iterator,&GeometryCore::begin_TPC_id,&GeometryCore::end_TPC_id > IterateTPCIDs() const
Enables ranged-for loops on all TPC IDs of the detector.
bool StartTraj(Trajectory &tj, const unsigned int &fromHit, const unsigned int &toHit, const unsigned short &pass)
label of module producing input hits
void StudyShowerParents(HistStuff &hist)
Definition: TCTruth.cxx:235
void FillGaps(Trajectory &tj)
label of module producing input hits
bool SignalAtTp(TjStuff &tjs, const TrajPoint &tp)
Definition: Utils.cxx:1639
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
calo::CalorimetryAlg * caloAlg
Definition: DataStructs.h:528
void FindPFParticles(TjStuff &tjs, const geo::TPCID &tpcid, bool prt)
Definition: PFPUtils.cxx:1968
unsigned short Pass
the pass on which it was created
Definition: DataStructs.h:187
void TagDeltaRays(TjStuff &tjs, const CTP_t &inCTP)
Definition: Utils.cxx:2985
void CheckTrajBeginChg(TjStuff &tjs, unsigned short itj, bool prt)
Definition: Utils.cxx:1272
int TDCtick_t
Type representing a TDC tick.
Definition: RawTypes.h:24
bool TrajIsClean(TjStuff &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:2814
bool fUseOldBackTracker
label of module producing input hits
std::vector< float > EndTim
Definition: DataStructs.h:339
std::vector< int > ShowerID
Definition: DataStructs.h:350
void ReleaseHits(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:1070
TMVA::Reader * ShowerParentReader
Definition: DataStructs.h:529
void ReverseTraj(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:2666
void DefineShTree(TTree *t)
label of module producing input hits
std::vector< unsigned int > fAlgModCount
label of module producing input hits
unsigned int Hit
set to the hit index in fHits if a Plane:Wire:Tick match is found
Definition: DebugStruct.h:33
std::vector< ShowerStruct > cots
Definition: DataStructs.h:506
std::vector< float > ShowerTag
[min MCSMom, max separation, min # Tj < separation] for a shower tag
Definition: DataStructs.h:515
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
std::vector< std::string > StageName
Definition: DataStructs.h:353
bool AttachTrajToVertex(TjStuff &tjs, Trajectory &tj, VtxStore &vx, bool prt)
Definition: TCVertex.cxx:1712
static bool SortByMultiplet(TCHit const &a, TCHit const &b)
label of module producing input hits
int Cryostat
Select Cryostat.
Definition: DebugStruct.h:27
void FindNeutralVertices(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:369
struct of temporary 3D vertices
Definition: DataStructs.h:111
Int_t max
Definition: plot.C:27
void HiEndDelta(Trajectory &tj)
label of module producing input hits
unsigned int EventsProcessed
Definition: DataStructs.h:521
bool StopIfBadFits(Trajectory &tj)
label of module producing input hits
bool CheckWireHitRange(const TjStuff &tjs)
Definition: Utils.cxx:3855
std::vector< int > TjID
Definition: DataStructs.h:348
TCanvas * c1
Definition: plotHisto.C:7
int Wire
Select hit Wire for debugging.
Definition: DebugStruct.h:31
TCanvas * c2
Definition: plot_hist.C:75
TH1F * fVx2Score
Definition: TCHist.h:61
int WorkID
Select the StartWorkID for debugging.
Definition: DebugStruct.h:34
const geo::GeometryCore * geom
Definition: DataStructs.h:526
std::vector< short > BeginVtx
Definition: DataStructs.h:337
void FindUseHits(Trajectory &tj, unsigned short ipt, float maxDelta, bool useChg)
label of module producing input hits
geo::TPCID TPCID
Definition: DataStructs.h:120
DebugStuff debug
Definition: DebugStruct.cxx:4
void MakeJunkVertices(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:14
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:170
void UpdateTjChgProperties(std::string inFcnLabel, TjStuff &tjs, Trajectory &tj, bool prt)
Definition: Utils.cxx:3118
void MatchTrueHits()
Definition: TCTruth.cxx:31
void GetHitCollection(const art::Event &evt)
label of module producing input hits
std::vector< unsigned int > LastWire
the last wire with a hit
Definition: DataStructs.h:474
void RunTrajClusterAlg(const art::Event &evt)
label of module producing input hits
std::vector< VtxStore > vtx
2D vertices
Definition: DataStructs.h:502
bool MakeJunkTraj(std::vector< unsigned int > tHits, bool prt)
label of module producing input hits
const std::vector< std::string > StopFlagNames
Definition: DataStructs.cxx:71
parameter set interface
void VtxHitsSwap(TjStuff &tjs, const CTP_t inCTP)
Definition: TCVertex.cxx:2758
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:169
bool SignalBetween(TjStuff &tjs, const TrajPoint &tp1, const TrajPoint &tp2, const float &MinWireSignalFraction, bool prt)
Definition: Utils.cxx:1495
std::vector< float > NeutralVxCuts
Definition: DataStructs.h:512
unsigned int CreateHit(TCHit tcHit)
label of module producing input hits
void Find2DVertices(TjStuff &tjs, const CTP_t &inCTP)
Definition: TCVertex.cxx:114
T get(std::string const &key) const
Definition: ParameterSet.h:231
int Plane
Select plane.
Definition: DebugStruct.h:29
std::vector< short > fMinMCSMom
Min MCSMom for each pass.
std::vector< unsigned short > fMaxAngleCode
max allowed angle code for each pass
float TrajLength(Trajectory &tj)
Definition: Utils.cxx:2204
std::vector< Trajectory > allTraj
vector of all trajectories in each plane
Definition: DataStructs.h:493
std::vector< short > EndVtx
Definition: DataStructs.h:342
void PFPVertexCheck(TjStuff &tjs)
Definition: PFPUtils.cxx:2418
void StepCrawl(Trajectory &tj)
label of module producing input hits
std::bitset< 64 > UseAlg
Allow user to mask off specific algorithms.
Definition: DataStructs.h:525
unsigned short LocalIndex
Definition: DataStructs.h:209
iterator begin()
Definition: ParticleList.h:305
std::array< unsigned short, 2 > EndPt
First and last point in the trajectory that has charge.
Definition: DataStructs.h:182
void DefineCRTree(TTree *t)
label of module producing input hits
unsigned short PDGCode
shower-like or track-like {default is track-like}
Definition: DataStructs.h:185
float HitTimeErr(const unsigned int iht)
label of module producing input hits
void getManyByType(std::vector< Handle< PROD >> &results) const
Definition: DataViewImpl.h:446
std::vector< int > cr_origin
Definition: DataStructs.h:367
virtual void reconfigure(fhicl::ParameterSet const &pset)
label of module producing input hits
std::vector< recob::Hit > YieldHits()
Returns (and loses) the art::Ptr collection of previously reconstructed hits (e.g. gaushit)
void SaveCRInfo(TjStuff &tjs, PFPStruct &pfp, bool prt, bool fIsRealData)
Definition: TCCR.cxx:8
std::vector< float > MaxPos0
Definition: DataStructs.h:471
void DefineTjParents(TjStuff &tjs, const geo::TPCID &tpcid, bool prt)
Definition: Utils.cxx:17
void FillmAllTraj(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: PFPUtils.cxx:127
trkf::LinFitAlg fLinFitAlg
label of module producing input hits
bool get_if_present(std::string const &key, T &value) const
Definition: ParameterSet.h:208
std::vector< unsigned int > FirstWire
the first wire with a hit
Definition: DataStructs.h:473
double PosSep2(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1631
bool SaveShowerTree
Definition: DataStructs.h:486
bool fStudyMode
study cuts
std::vector< float > Envelope
Definition: DataStructs.h:356
Point2_t Pos
Definition: DataStructs.h:84
std::vector< float > fQualityCuts
Min points/wire, min consecutive pts after a gap.
void AddLAHits(Trajectory &tj, unsigned short ipt, bool &sigOK)
label of module producing input hits
EventNumber_t event() const
Definition: Event.h:67
bool MakeBareTrajPoint(const TjStuff &tjs, unsigned int fromHit, unsigned int toHit, TrajPoint &tp)
Definition: Utils.cxx:3493
std::vector< MatchStruct > matchVec
3D matching vector
Definition: DataStructs.h:504
bool TestBeam
Expect tracks entering from the front face. Don&#39;t create neutrino PFParticles.
Definition: DataStructs.h:534
std::string PrintHit(const TCHit &hit)
Definition: Utils.cxx:4732
void ReconstructAllTraj(CTP_t inCTP)
label of module producing input hits
TMVA::Reader fMVAReader
label of module producing input hits
float UnitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:468
bool has_key(std::string const &key) const
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
void MatchTruth(const HistStuff &hist, bool fStudyMode)
Definition: TCTruth.cxx:459
raw::TDCtick_t StartTick() const
Initial tdc tick for hit.
Definition: Hit.h:217
View_t View(geo::PlaneID const &pid) const
Returns the view (wire orientation) on the channels of specified TPC plane.
geo::TPCID TPCID
Definition: DataStructs.h:469
unsigned short NumHitsInTP(const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3677
void TagMuonDirections(TjStuff &tjs, short debugWorkID)
Definition: Utils.cxx:3078
bool fIsRealData
label of module producing input hits
void ScoreVertices(TjStuff &tjs, const geo::TPCID &tpcid, bool prt)
Definition: TCVertex.cxx:2193
float PointTrajDOCA(TjStuff const &tjs, unsigned int iht, TrajPoint const &tp)
Definition: Utils.cxx:2139
bool fQuitAlg
label of module producing input hits
std::vector< float > BeginChg
Definition: DataStructs.h:336
bool didPrt
label of module producing input hits
std::vector< float > MatchTruth
Match to MC truth.
Definition: DataStructs.h:518
std::array< unsigned short, 2 > VtxID
ID of 2D vertex.
Definition: DataStructs.h:181
Detector simulation of raw signals on wires.
short MCSMom(const TjStuff &tjs, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2837
void MaskTrajEndPoints(Trajectory &tj, unsigned short nPts)
label of module producing input hits
std::vector< DontClusterStruct > dontCluster
Definition: DataStructs.h:507
std::vector< Vtx3Store > vtx3
3D vertices
Definition: DataStructs.h:503
raw::TDCtick_t EndTick() const
Final tdc tick for hit.
Definition: Hit.h:218
float ChgFracNearPos(TjStuff &tjs, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2614
void CheckHiMultEndHits(Trajectory &tj)
label of module producing input hits
void DefineHit(TCHit &tcHit, CTP_t &hitCTP, unsigned int &hitWire)
label of module producing input hits
void EndMerge(CTP_t inCTP, bool lastPass)
label of module producing input hits
float fMinAmp
min amplitude required for declaring a wire signal is present
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
std::bitset< 16 > Stat
Vertex status bits using kVtxBit_t.
Definition: DataStructs.h:97
std::vector< int > EnvPlane
Definition: DataStructs.h:357
int ID
set to 0 if killed
Definition: DataStructs.h:93
std::array< int, 3 > Vx2ID
Definition: DataStructs.h:121
unsigned short AngleCode
Definition: DataStructs.h:161
void ReversePropagate(Trajectory &tj)
label of module producing input hits
void SplitHiChgHits(Trajectory &tj)
label of module producing input hits
std::vector< short > MCSMom
Definition: DataStructs.h:344
int Tick
Select hit PeakTime for debugging (< 0 for vertex finding)
Definition: DebugStruct.h:32
void TrajIntersection(TrajPoint const &tp1, TrajPoint const &tp2, Point2_t &pos)
Definition: Utils.cxx:2166
int TPC
Select TPC.
Definition: DebugStruct.h:28
float val
Definition: Utils.cxx:8
raw::ChannelID_t PlaneWireToChannel(WireID const &wireid) const
Returns the ID of the TPC channel connected to the specified wire.
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:219
std::vector< short > MuonTag
min length and min MCSMom for a muon tag
Definition: DataStructs.h:514
void FixTrajBegin(Trajectory &tj)
label of module producing input hits
unsigned int CTP_t
Definition: DataStructs.h:41
std::vector< float > VertexScoreWeights
Definition: DataStructs.h:511
bool fMaskedLastTP
label of module producing input hits
std::vector< int > StageNum
Definition: DataStructs.h:352
bool InTrajOK(TjStuff &tjs, std::string someText)
Definition: Utils.cxx:1199
std::vector< std::vector< std::pair< int, int > > > WireHitRange
Definition: DataStructs.h:499
bool FillWireHitRange(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: Utils.cxx:3722
std::vector< TCHit > fHits
Definition: DataStructs.h:495
TDirectory * dir
Definition: macro.C:5
bool fTryWithNextPass
label of module producing input hits
bool fGoodTraj
label of module producing input hits
Int_t min
Definition: plot.C:26
unsigned short nPlanes
Definition: DataStructs.h:362
void PrintShowers(std::string fcnLabel, TjStuff &tjs)
Definition: TCShower.cxx:4676
short StepDir
-1 = going US (-> small wire#), 1 = going DS (-> large wire#)
Definition: DataStructs.h:188
int TJPrt
label of module producing input hits
TH1F * fVx3Score
Definition: TCHist.h:62
unsigned short NTraj
Definition: DataStructs.h:86
std::vector< float > BeginWir
Definition: DataStructs.h:333
geo::PlaneID DecodeCTP(CTP_t CTP)
Definition: DataStructs.cxx:89
std::vector< float > Match3DCuts
3D matching cuts
Definition: DataStructs.h:517
float ExpectedHitsRMS(TjStuff &tjs, const TrajPoint &tp)
Definition: Utils.cxx:1627
TTree * showertree
label of module producing input hits
std::vector< float > EndChg
Definition: DataStructs.h:341
void LinFit(std::vector< float > &x, std::vector< float > &y, std::vector< float > &ey2, float &Intercept, float &Slope, float &InterceptError, float &SlopeError, float &ChiDOF)
Definition: LinFitAlg.cxx:26
Int_t ipart
Definition: Style.C:10
double PosSep(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1625
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
void FindMissedVxTjs(const geo::TPCID &tpcid)
label of module producing input hits
void KillPoorVertices(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCVertex.cxx:2233
bool prt
label of module producing input hits
void SetVx2Score(TjStuff &tjs, bool prt)
Definition: TCVertex.cxx:2322
void SetEndPoints(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:2788
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:45
unsigned short NumPtsWithCharge(const TjStuff &tjs, const Trajectory &tj, bool includeDeadWires)
Definition: Utils.cxx:1738
std::vector< simb::MCParticle * > MCPartList
Definition: DataStructs.h:520
bool MergeAndStore(TjStuff &tjs, unsigned int itj1, unsigned int itj2, bool doPrt)
Definition: Utils.cxx:3896
void Finish3DShowers(TjStuff &tjs)
Definition: TCShower.cxx:131
raw::TDCtick_t StartTick
Definition: DataStructs.h:196
std::vector< ClusterStore > tcl
the clusters we are creating
Definition: DataStructs.h:501
float MaxHitDelta(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:2649
unsigned short TPNearVertex(TjStuff &tjs, const TrajPoint &tp)
Definition: TCVertex.cxx:1637
HistStuff hist
label of module producing input hits
float PeakTime
Definition: DataStructs.h:198
std::vector< float > cr_pfpxmin
Definition: DataStructs.h:368
std::string PrintStopFlag(const Trajectory &tj, unsigned short end)
Definition: Utils.cxx:4707
std::vector< short > DeltaRayTag
min length, min MCSMom and min separation (WSE) for a delta ray tag
Definition: DataStructs.h:513
std::vector< Tj2Pt > mallTraj
vector of trajectory points ordered by increasing X
Definition: DataStructs.h:494
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
float SigmaPeakTime() const
Uncertainty for the signal peak, in tick units.
Definition: Hit.h:220
size_type get(size_type i, reference item, data_reference data) const
Definition: FindManyP.h:469
void PrintPFPs(std::string someText, const TjStuff &tjs)
Definition: Utils.cxx:4690
void UnsetUsedHits(TjStuff &tjs, TrajPoint &tp)
Definition: Utils.cxx:1082
void SetPDGCode(TjStuff &tjs, unsigned short itj)
Definition: Utils.cxx:3697
float fMultHitSep
preferentially "merge" hits with < this separation
std::vector< float > AngleRanges
list of max angles for each angle range
Definition: DataStructs.h:500
float HitsPosTick(TjStuff &tjs, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:3645
kink found in CheckTraj
Definition: DataStructs.h:379
bool EraseHit(const unsigned int &delHit)
label of module producing input hits
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2351
std::vector< int > EnvShowerID
Definition: DataStructs.h:359
bool StoreVertex(TjStuff &tjs, VtxStore &vx)
Definition: TCVertex.cxx:1911
void MaskBadTPs(Trajectory &tj, float const &maxChi)
label of module producing input hits
std::vector< float > KinkCuts
kink angle, nPts fit, (alternate) kink angle significance
Definition: DataStructs.h:516
float MCSThetaRMS(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:2907
void FixTrajEnd(Trajectory &tj, unsigned short atPt)
label of module producing input hits
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:27
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
float fJTMaxHitSep2
label of module producing input hits
bool TrajTrajDOCA(const TjStuff &tjs, const Trajectory &tj1, const Trajectory &tj2, unsigned short &ipt1, unsigned short &ipt2, float &minSep)
Definition: Utils.cxx:2046
bool StoreTraj(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:1095
float TPHitsRMSTick(TjStuff &tjs, TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3582
std::vector< int > IsShowerTj
Definition: DataStructs.h:349
RunNumber_t run() const
Definition: Event.h:77
void ClearCRInfo(TjStuff &tjs)
Definition: TCCR.cxx:117
void PrintTrajPoint(std::string someText, const TjStuff &tjs, unsigned short ipt, short dir, unsigned short pass, TrajPoint const &tp)
Definition: Utils.cxx:4574
std::vector< float > Vertex3DCuts
2D vtx -> 3D vtx matching cuts
Definition: DataStructs.h:510
std::vector< short > PlaneNum
Definition: DataStructs.h:346
bool MaskedHitsOK(Trajectory &tj)
label of module producing input hits
void GottaKink(Trajectory &tj, unsigned short &killPts)
label of module producing input hits
void FindVtxTjs(CTP_t inCTP)
label of module producing input hits
void DefinePFPParents(TjStuff &tjs, const geo::TPCID &tpcid, bool prt)
Definition: PFPUtils.cxx:2441
void PrintResults(int eventNum) const
Definition: TCTruth.cxx:1083
float fMaxWireSkipWithSignal
max number of wires to skip with a signal on them
void PrintHeader(std::string someText)
Definition: Utils.cxx:4568
void UseUnusedHits()
label of module producing input hits
bool NeedsUpdate
Set true when the Tj needs to be updated (only for the TP Environment right now)
Definition: DataStructs.h:191
void TrajPointTrajDOCA(TjStuff &tjs, TrajPoint const &tp, Trajectory const &tj, unsigned short &closePt, float &minSep)
Definition: Utils.cxx:2026
Point2_t HitPos
Definition: DataStructs.h:146
art::InputTag fHitTruthModuleLabel
label of module producing MCParticle -> hit associations
bool DebugMode
print additional info when in debug mode
Definition: DataStructs.h:535
std::vector< unsigned int > NumWires
Definition: DataStructs.h:470
bool FindShowers3D(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: TCShower.cxx:286
don&#39;t mess with this line
Definition: DataStructs.h:439
short StepDir
the normal user-defined stepping direction = 1 (US -> DS) or -1 (DS -> US)
Definition: DataStructs.h:531
CTP_t CTP
set to an invalid CTP
Definition: DebugStruct.h:30
TTree * crtree
label of module producing input hits
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:230
double HitPosErr2
Definition: DataStructs.h:149
bool IsGhost(std::vector< unsigned int > &tHits, unsigned short &ofTraj)
label of module producing input hits
bool valIncreasing(SortEntry c1, SortEntry c2)
const art::Ptr< simb::MCTruth > & TrackIdToMCTruth_P(int const &id)
void FitTraj(TjStuff &tjs, Trajectory &tj)
Definition: Utils.cxx:673
void TagShowerLike(std::string inFcnLabel, TjStuff &tjs, const CTP_t &inCTP)
Definition: TCShower.cxx:3700
struct of temporary clusters
Definition: DataStructs.h:65
int fWorkID
label of module producing input hits
const detinfo::DetectorProperties * detprop
Definition: DataStructs.h:527