LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
StepUtils.cxx
Go to the documentation of this file.
2 
3 
4 struct SortEntry{
5  unsigned int index;
6  float val;
7 };
8 
9 bool valsDecreasing (SortEntry c1, SortEntry c2) { return (c1.val > c2.val);}
10 //bool valIncreasing (SortEntry c1, SortEntry c2) { return (c1.val < c2.val);}
11 
12 namespace tca {
13 
15  void StepAway(TCSlice& slc, Trajectory& tj)
16  {
17  // Step along the direction specified in the traj vector in steps of size step
18  // (wire spacing equivalents). Find hits between the last trajectory point and
19  // the last trajectory point + step. A new trajectory point is added if hits are
20  // found. Stepping continues until no signal is found for two consecutive steps
21  // or until a wire or time boundary is reached.
22 
23  tj.IsGood = false;
24  if(tj.Pts.empty()) return;
25 
26  unsigned short plane = DecodeCTP(tj.CTP).Plane;
27 
28  unsigned short lastPtWithUsedHits = tj.EndPt[1];
29 
30  unsigned short lastPt = lastPtWithUsedHits;
31  // Construct a local TP from the last TP that will be moved on each step.
32  // Only the Pos and Dir variables will be used
33  TrajPoint ltp;
34  ltp.CTP = tj.CTP;
35  ltp.Pos = tj.Pts[lastPt].Pos;
36  ltp.Dir = tj.Pts[lastPt].Dir;
37  // A second TP is cloned from the leading TP of tj, updated with hits, fit
38  // parameters,etc and possibly pushed onto tj as the next TP
39  TrajPoint tp;
40 
41  // assume it is good from here on
42  tj.IsGood = true;
43 
44  unsigned short nMissedSteps = 0;
45  // Use MaxChi chisq cut for stiff trajectories
46  bool useMaxChiCut = (tj.PDGCode == 13 || !tj.Strategy[kSlowing]);
47 
48  // Get the first forecast when there are 6 points with charge
49  tjfs.resize(1);
50  tjfs[0].nextForecastUpdate = 6;
51 
52  for(unsigned short step = 1; step < 10000; ++step) {
53  // Get a forecast of what is ahead.
54 // unsigned short span = tj.EndPt[1] - tj.EndPt[0] + 1;
55  unsigned short span = NumPtsWithCharge(slc, tj, false);
56  if(tcc.doForecast && !tj.AlgMod[kRvPrp] && span == tjfs[tjfs.size() - 1].nextForecastUpdate) {
57  Forecast(slc, tj);
58  SetStrategy(slc, tj);
59  SetPDGCode(slc, tj, false);
60  }
61  // make a copy of the previous TP
62  lastPt = tj.Pts.size() - 1;
63  tp = tj.Pts[lastPt];
64  ++tp.Step;
65  double stepSize = tcc.VLAStepSize;
66  if(tp.AngleCode < 2) stepSize = std::abs(1/ltp.Dir[0]);
67  // move the local TP position by one step in the right direction
68  for(unsigned short iwt = 0; iwt < 2; ++iwt) ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
69 
70  unsigned short ivx = TPNearVertex(slc, ltp);
71  if(ivx != USHRT_MAX) {
72  // Trajectory stops near a vertex so make the assignment
73  if(AttachTrajToVertex(slc, tj, slc.vtxs[ivx], tcc.dbgStp || tcc.dbg2V)) tj.StopFlag[1][kAtVtx] = true;
74  break;
75  }
76 
77  // copy this position into tp
78  tp.Pos = ltp.Pos;
79  tp.Dir = ltp.Dir;
80  if(tcc.dbgStp) {
81  mf::LogVerbatim myprt("TC");
82  myprt<<"StepAway "<<step<<" Pos "<<tp.Pos[0]<<" "<<tp.Pos[1]<<" Dir "<<tp.Dir[0]<<" "<<tp.Dir[1]<<" stepSize "<<stepSize<<" AngCode "<<tp.AngleCode<<" Strategy";
83  for(unsigned short ibt = 0; ibt < StrategyBitNames.size(); ++ibt) {
84  if(tj.Strategy[ibt]) myprt<<" "<<StrategyBitNames[ibt];
85  } // ib
86  } // tcc.dbgStp
87  // hit the boundary of the TPC?
88  if(tp.Pos[0] < 0 || tp.Pos[0] > tcc.maxPos0[plane] ||
89  tp.Pos[1] < 0 || tp.Pos[1] > tcc.maxPos1[plane]) break;
90  // remove the old hits and other stuff
91  tp.Hits.clear();
92  tp.UseHit.reset();
93  tp.FitChi = 0; tp.Chg = 0;
94  // append to the trajectory
95  tj.Pts.push_back(tp);
96  // update the index of the last TP
97  lastPt = tj.Pts.size() - 1;
98  // look for hits
99  bool sigOK = false;
100  AddHits(slc, tj, lastPt, sigOK);
101  // Check the stop flag
102  if(tj.StopFlag[1][kAtTj]) break;
103  // If successfull, AddHits has defined UseHit for this TP,
104  // set the trajectory endpoints, and define HitPos.
105  if(tj.Pts[lastPt].Hits.empty()) {
106  // Require three points with charge on adjacent wires for small angle
107  // stepping.
108  if(tj.Pts[lastPt].AngleCode == 0 && lastPt == 2) return;
109  // No close hits added.
110  ++nMissedSteps;
111  // First check for no signal in the vicinity
112  if(lastPt > 0) {
113  // break if this is a reverse propagate activity and there was no signal (not on a dead wire)
114  if(!sigOK && tj.AlgMod[kRvPrp]) break;
115  // Ensure that there is a signal here after missing a number of steps on a LA trajectory
116  if(tj.Pts[lastPt].AngleCode > 0 && nMissedSteps > 4 && !SignalAtTp(slc, ltp)) {
117  tj.StopFlag[1][kSignal] = false;
118  break;
119  }
120  // the last point with hits (used or not) is the previous point
121  unsigned short lastPtWithHits = lastPt - 1;
122  float tps = TrajPointSeparation(tj.Pts[lastPtWithHits], ltp);
123  float dwc = DeadWireCount(slc, ltp, tj.Pts[lastPtWithHits]);
124  float nMissedWires = tps * std::abs(ltp.Dir[0]) - dwc;
125  float maxWireSkip = tcc.maxWireSkipNoSignal;
126  if(tj.PDGCode == 13) maxWireSkip = tcc.muonTag[2];
127  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" StepAway: no signal at ltp "<<PrintPos(slc, ltp)<<" nMissedWires "<<std::fixed<<std::setprecision(1)<<nMissedWires<<" dead wire count "<<dwc<<" maxWireSkip "<<maxWireSkip<<" tj.PDGCode "<<tj.PDGCode;
128  if(nMissedWires > maxWireSkip) {
129  // We passed a number of wires without adding hits and are ready to quit.
130  // First see if there is one good unused hit on the end TP and if so use it
131  // lastPtWithHits + 1 == lastPt && tj.Pts[lastPtWithHits].Chg == 0 && tj.Pts[lastPtWithHits].Hits.size() == 1
132  if(tj.EndPt[1] < tj.Pts.size() - 1 && tj.Pts[tj.EndPt[1]+1].Hits.size() == 1) {
133  unsigned short lastLonelyPoint = tj.EndPt[1] + 1;
134  unsigned int iht = tj.Pts[lastLonelyPoint].Hits[0];
135  if(slc.slHits[iht].InTraj == 0 && tj.Pts[lastLonelyPoint].Delta < 3 * tj.Pts[lastLonelyPoint].DeltaRMS) {
136  slc.slHits[iht].InTraj = tj.ID;
137  tj.Pts[lastLonelyPoint].UseHit[0] = true;
138  DefineHitPos(slc, tj.Pts[lastLonelyPoint]);
139  SetEndPoints(tj);
140  if(tcc.dbgStp) {
141  mf::LogVerbatim("TC")<<" Added a Last Lonely Hit before breaking ";
142  PrintTrajPoint("LLH", slc, lastPt, tj.StepDir, tj.Pass, tj.Pts[lastLonelyPoint]);
143  }
144  }
145  }
146  break;
147  }
148  } // lastPt > 0
149  // no sense keeping this TP on tj if no hits were added
150  tj.Pts.pop_back();
151  continue;
152  } // tj.Pts[lastPt].Hits.empty()
153  // ensure that we actually moved
154  if(lastPt > 0 && PosSep2(tj.Pts[lastPt].Pos, tj.Pts[lastPt-1].Pos) < 0.1) return;
155  // Found hits at this location so reset the missed steps counter
156  nMissedSteps = 0;
157  // Update the last point fit, etc using the just added hit(s)
158  UpdateTraj(slc, tj);
159  // a failure occurred
160  if(tj.NeedsUpdate) return;
161  if(tj.Pts[lastPt].Chg == 0) {
162  // There are points on the trajectory by none used in the last step. See
163  // how long this has been going on
164  float tps = TrajPointSeparation(tj.Pts[tj.EndPt[1]], ltp);
165  float dwc = DeadWireCount(slc, ltp, tj.Pts[tj.EndPt[1]]);
166  float nMissedWires = tps * std::abs(ltp.Dir[0]) - dwc;
167  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Hits exist on the trajectory but are not used. Missed wires "<<std::nearbyint(nMissedWires)<<" dead wire count "<<(int)dwc;
168  // break if this is a reverse propagate activity with no dead wires
169  if(tj.AlgMod[kRvPrp] && dwc == 0) break;
170  if(nMissedWires > tcc.maxWireSkipWithSignal) break;
171  // try this out
172  if(!MaskedHitsOK(slc, tj)) {
173  return;
174  }
175  // check for a series of bad fits and stop stepping
176  if(tcc.useAlg[kStopBadFits] && nMissedWires > 4 && StopIfBadFits(slc, tj)) break;
177  // Keep stepping
178  if(tcc.dbgStp) {
179  if(tj.AlgMod[kRvPrp]) {
180  PrintTrajectory("RP", slc, tj, lastPt);
181  } else {
182  PrintTrajectory("SC", slc, tj, lastPt);
183  }
184  }
185  continue;
186  } // tp.Hits.empty()
187  if(tj.Pts.size() == 3) {
188  // ensure that the last hit added is in the same direction as the first two.
189  // This is a simple way of doing it
190  bool badTj = (PosSep2(tj.Pts[0].HitPos, tj.Pts[2].HitPos) < PosSep2(tj.Pts[0].HitPos, tj.Pts[1].HitPos));
191  // ensure that this didn't start as a small angle trajectory and immediately turn
192  // into a large angle one
193  if(!badTj && tj.Pts[lastPt].AngleCode > tcc.maxAngleCode[tj.Pass]) badTj = true;
194  // check for a large change in angle
195  if(!badTj) {
196  float dang = DeltaAngle(tj.Pts[0].Ang, tj.Pts[2].Ang);
197  if(dang > 0.5) badTj = false;
198  }
199  //check for a wacky delta
200  if(!badTj && tj.Pts[2].Delta > 2) badTj = true;
201  if(badTj) {
202  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Bad Tj found on the third point. Quit stepping.";
203  tj.IsGood = false;
204  return;
205  }
206  } // tj.Pts.size() == 3
207  // Update the local TP with the updated position and direction
208  ltp.Pos = tj.Pts[lastPt].Pos;
209  ltp.Dir = tj.Pts[lastPt].Dir;
210  if(tj.MaskedLastTP) {
211  // see if TPs have been masked off many times and if the
212  // environment is clean. If so, return and try with next pass
213  // cuts
214  if(!MaskedHitsOK(slc, tj)) {
215  if(tcc.dbgStp) {
216  if(tj.AlgMod[kRvPrp]) {
217  PrintTrajectory("RP", slc, tj, lastPt);
218  } else {
219  PrintTrajectory("SC", slc, tj, lastPt);
220  }
221  }
222  return;
223  }
224  // Don't bother with the rest of the checking below if we
225  // set all hits not used on this TP
226  if(tcc.dbgStp) {
227  if(tj.AlgMod[kRvPrp]) {
228  PrintTrajectory("RP", slc, tj, lastPt);
229  } else {
230  PrintTrajectory("SC", slc, tj, lastPt);
231  }
232  }
233  continue;
234  }
235  // We have added a TP with hits
236  // assume that we aren't going to kill the point we just added, or any
237  // of the previous points...
238  unsigned short killPts = 0;
239  // assume that we should keep going after killing points
240  bool keepGoing = true;
241  // check for a kink. Stop crawling if one is found
242  GottaKink(slc, tj, killPts);
243  if(tj.StopFlag[1][kAtKink]) {
244  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" stop at kink with killPts "<<killPts;
245  keepGoing = false;
246  }
247  // See if the Chisq/DOF exceeds the maximum.
248  // UpdateTraj should have reduced the number of points fit
249  // as much as possible for this pass, so this trajectory is in trouble.
250  if(killPts == 0 && tj.Pts[lastPt].FitChi > tcc.maxChi && useMaxChiCut) {
251  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" bad FitChi "<<tj.Pts[lastPt].FitChi<<" cut "<<tcc.maxChi;
252  if(tcc.useAlg[kNewStpCuts]) {
253  // remove the last point before quitting
254  UnsetUsedHits(slc, tj.Pts[lastPt]);
255  SetEndPoints(tj);
256  }
257  tj.IsGood = (NumPtsWithCharge(slc, tj, true) > tcc.minPtsFit[tj.Pass]);
258  return;
259  }
260  // print the local tp unless we have killing to do
261  if(killPts == 0) {
262  if(tcc.dbgStp) {
263  if(tj.AlgMod[kRvPrp]) {
264  PrintTrajectory("RP", slc, tj, lastPt);
265  } else {
266  PrintTrajectory("SC", slc, tj, lastPt);
267  }
268  }
269  } else {
270  MaskTrajEndPoints(slc, tj, killPts);
271  if(!tj.IsGood) return;
272  unsigned int onWire = (float)(std::nearbyint(tj.Pts[lastPt].Pos[0]));
273  float nSteps = (float)(step - tj.Pts[lastPt - killPts].Step);
274  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"TRP killing "<<killPts<<" after "<<nSteps<<" steps from prev TP. Current tp.Pos "<<tp.Pos[0]<<" "<<tp.Pos[1];
275  // move the position
276  tj.Pts[lastPt].Pos[0] += nSteps * tj.Pts[lastPt].Dir[0];
277  tj.Pts[lastPt].Pos[1] += nSteps * tj.Pts[lastPt].Dir[1];
278  if(tj.Pts[lastPt].AngleCode == 0) {
279  // put the TP at the wire position prior to the move
280  float dw = onWire - tj.Pts[lastPt].Pos[0];
281  tj.Pts[lastPt].Pos[0] = onWire;
282  tj.Pts[lastPt].Pos[1] += dw * tj.Pts[lastPt].Dir[1] / tj.Pts[lastPt].Dir[0];
283  }
284  // check the MCSMom after we going
285  if(tj.Pts.size() > 20 && tj.Pass < tcc.minMCSMom.size() && tj.MCSMom < tcc.minMCSMom[tj.Pass]) break;
286  // copy to the local trajectory point
287  ltp.Pos = tj.Pts[lastPt].Pos;
288  ltp.Dir = tj.Pts[lastPt].Dir;
289  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" New ltp.Pos "<<ltp.Pos[0]<<" "<<ltp.Pos[1]<<" ticks "<<(int)ltp.Pos[1]/tcc.unitsPerTick;
290  if(!keepGoing) break;
291  }
292  } // step
293 
294  // Do a more carefull treatment
295  // This is the wrong place to do this.
296 // SetPDGCode(slc, tj, true);
297 
298  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"End StepAway with tj size "<<tj.Pts.size()<<" isGood = "<<tj.IsGood;
299 
300  } // StepAway
301 
304  {
305  // Determine if the tracking strategy is appropriate and make some tweaks if it isn't
306  if(tjfs.empty()) return;
307  // analyze the last forecast
308  auto& tjf = tjfs[tjfs.size() - 1];
309 
310  auto& lastTP = tj.Pts[tj.EndPt[1]];
311  // Stay in Slowing strategy if we are in it and reduce the number of points fit further
312  if(tj.Strategy[kSlowing]) {
313  lastTP.NTPsFit = 5;
314  return;
315  }
316 
317  float npwc = NumPtsWithCharge(slc, tj, false);
318  bool tkLike = (tjf.outlook < 1.5);
319  // A showering-electron-like trajectory
320  bool shLike = (tjf.outlook > 2 && tjf.chgSlope > 0);
321  // This cut picks up a 240 MeV electron
322  if(!shLike) shLike = tjf.showerLikeFraction > 0.4;
323  float momRat = 0;
324  if(tj.MCSMom > 0) momRat = (float)tjf.MCSMom / (float)tj.MCSMom;
325  if(tcc.dbgStp) {
326  mf::LogVerbatim myprt("TC");
327  myprt<<"SetStrategy: npwc "<<npwc<<" outlook "<<tjf.outlook;
328  myprt<<" tj MCSMom "<<tj.MCSMom<<" forecast MCSMom "<<tjf.MCSMom;
329  myprt<<" momRat "<<std::fixed<<std::setprecision(2)<<momRat;
330  myprt<<" tkLike? "<<tkLike<<" showerLike? "<<shLike;
331  myprt<<" leavesBeforeEnd? "<<tjf.leavesBeforeEnd<<" endBraggPeak? "<<tjf.endBraggPeak;
332  }
333  if(tjf.outlook < 0) return;
334  // Look for a long clean muon in the forecast
335  if(tkLike && tj.MCSMom > 200 && tjf.MCSMom > 800 && tjf.nextForecastUpdate > 50 && tjf.chgFitChiDOF < 10) {
336  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: Use the StiffMu strategy";
337  tj.Strategy.reset();
338  tj.Strategy[kStiffMu] = true;
339  return;
340  } // StiffMu
341  bool notStiff = (!tj.Strategy[kStiffEl] && !tj.Strategy[kStiffMu]);
342  if(notStiff && !shLike && tj.MCSMom < 100 && tjf.MCSMom < 100) {
343  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: Low MCSMom. Use the Slowing Tj strategy";
344  tj.Strategy.reset();
345  tj.Strategy[kSlowing] = true;
346  lastTP.NTPsFit = 5;
347  return;
348  } // tracklike with > 1 forecast
349  if(notStiff && tkLike && tj.MCSMom < 200 && momRat < 0.7) {
350  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: Low MCSMom & low momRat. Use the Slowing Tj strategy";
351  tj.Strategy.reset();
352  tj.Strategy[kSlowing] = true;
353  lastTP.NTPsFit = 5;
354  return;
355  } // tracklike with > 1 forecast
356  if(!tjf.leavesBeforeEnd && tjf.endBraggPeak) {
357  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: Found a Bragg peak. Use the Slowing Tj strategy";
358  tj.Strategy.reset();
359  tj.Strategy[kSlowing] = true;
360  lastTP.NTPsFit = 5;
361  return;
362  } // tracklike with Bragg peak
363  if(npwc > 100 && tkLike && tjf.leavesBeforeEnd) {
364  // A long track-like trajectory that has many points fit and the outlook is track-like and
365  // it leaves the forecast polygon. Don't change the strategy but decrease the number of points fit
366  lastTP.NTPsFit /= 2;
367  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: Long track-like wandered out of forecast envelope. Reduce NTPsFit to "<<lastTP.NTPsFit;
368  return;
369  }
370  // a track-like trajectory that has high MCSMom in the forecast and hits a shower
371  if(tkLike && tjf.MCSMom > 600 && (tjf.foundShower || tjf.chgFitChiDOF > 20)) {
372  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: high MCSMom "<<tjf.MCSMom<<" and a shower ahead. Use the StiffEl strategy";
373  tj.Strategy.reset();
374  tj.Strategy[kStiffEl] = true;
375  // we think we know the direction (towards the shower) so startEnd is 0
376  tj.StartEnd = 0;
377  return;
378  } // Stiff electron
379  if(shLike) {
380  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: Inside a shower. Use the StiffEl strategy";
381  tj.Strategy.reset();
382  tj.Strategy[kStiffEl] = true;
383  // we think we know the direction (towards the shower) so startEnd is 0
384  tj.StartEnd = 0;
385  return;
386  }
387  // A curvy trajectory (not nessecarily track-like) with a consistent increase in charge
388  if(tkLike && tjf.MCSMom < 100 && !tjf.leavesBeforeEnd) {
389  // find the charge slope of the trajectory
390  float chgSlope, chgSlopeErr, chiDOF;
391  ChgSlope(slc, tj, chgSlope, chgSlopeErr, chiDOF);
392  // a significant increase in the charge in the reconstructed tj, which continues in the
393  // forecase polygon. TODO: use chgSlopeErr?
394  if(chgSlope > 3 * chgSlopeErr && (tjf.chgSlope > 3 * tjf.chgSlopeErr)) {
395  tj.Strategy.reset();
396  tj.Strategy[kSlowing] = true;
397  lastTP.NTPsFit = 5;
398  }
399  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"SetStrategy: Curvy? tj chgSlope "<<chgSlope<<" +/ "<<chgSlopeErr<<" forecast chgSlope "<<tjf.chgSlope<<" +/- "<<tjf.chgSlopeErr<<" Slowing? "<<tj.Strategy[kSlowing];
400  } // Slowing
401  } // SetStrategy
402 
404  void Forecast(TCSlice& slc, Trajectory& tj)
405  {
406  // Extrapolate the last TP of tj by many steps and return a forecast of what is ahead
407  // -1 error or not sure
408  // ~1 track-like with a slight chance of showers
409  // >2 shower-like
410  // nextForecastUpdate is set to the number of points on the trajectory when this function should
411  // be called again
412 
413  if(tj.Pts[tj.EndPt[1]].AngleCode == 2) return;
414  // add a new forecast
415  tjfs.resize(tjfs.size() + 1);
416  // assume there is insufficient info to make a decision
417  auto& tjf = tjfs[tjfs.size() - 1];
418  tjf.outlook = -1;
419  tjf.nextForecastUpdate = USHRT_MAX;
420 
421  unsigned short minShPts = 3;
422 
423  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
424  unsigned short istp = 0;
425  unsigned short nMissed = 0;
426 
427  bool doPrt = tcc.dbgStp;
428  // turn off annoying output from DefineHitPos
429  if(doPrt) tcc.dbgStp = false;
430  // find the minimum average TP charge. This will be used to calculate the
431  // 'effective number of hits' on a wire = total charge on the wire within the
432  // window / (minimum average TP charge). This is intended to reduce the sensitivity
433  // of this metric to the hit finder configuration
434  float minAveChg = 1E6;
435  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
436  if(tj.Pts[ipt].AveChg <= 0) continue;
437  if(tj.Pts[ipt].AveChg > minAveChg) continue;
438  minAveChg = tj.Pts[ipt].AveChg;
439  } // ipt
440  if(minAveChg <= 0 || minAveChg == 1E6) return;
441  // start a forecast Tj comprised of the points in the forecast envelope
442  Trajectory fctj;
443  fctj.CTP = tj.CTP;
444  fctj.ID = evt.WorkID;
445  // make a local copy of the last point
446  auto tp = tj.Pts[tj.EndPt[1]];
447  // Use the hits position instead of the fitted position so that a bad
448  // fit doesn't screw up the forecast.
449  float forecastWin0 = std::abs(tp.Pos[1] - tp.HitPos[1]);
450  if(forecastWin0 < 1) forecastWin0 = 1;
451  tp.Pos = tp.HitPos;
452  double stepSize = std::abs(1/tp.Dir[0]);
453  float window = 10 * stepSize;
454  if(doPrt) {
455  mf::LogVerbatim("TC")<<"Forecast T"<<tj.ID<<" PDGCode "<<tj.PDGCode<<" npwc "<<npwc<<" minAveChg "<<(int)minAveChg<<" stepSize "<<std::setprecision(2)<<stepSize<<" window "<<window;
456  mf::LogVerbatim("TC")<<" stp ___Pos____ nTPH Chg ChgPull Delta DRMS chgWid nTkLk nShLk";
457  }
458  unsigned short plane = DecodeCTP(tp.CTP).Plane;
459  float totHits = 0;
460  fctj.TotChg = 0;
461  float maxChg = 0;
462  unsigned short maxChgPt = 0;
463  unsigned short leavesNear = USHRT_MAX;
464  bool leavesBeforeEnd = false;
465  unsigned short showerStartNear = USHRT_MAX;
466  unsigned short showerEndNear = USHRT_MAX;
467  unsigned short nShLike = 0;
468  unsigned short nTkLike = 0;
469  unsigned short trimPts = 0;
470  for(istp = 0; istp < 1000; ++istp) {
471  // move the local TP position by one step in the right direction
472  for(unsigned short iwt = 0; iwt < 2; ++iwt) tp.Pos[iwt] += tp.Dir[iwt] * stepSize;
473  unsigned int wire = std::nearbyint(tp.Pos[0]);
474  if(wire < slc.firstWire[plane]) break;
475  if(wire > slc.lastWire[plane]-1) break;
476  MoveTPToWire(tp, (float)wire);
477  ++tp.Step;
478  if(FindCloseHits(slc, tp, window, kAllHits)) {
479  // Found hits or the wire is dead
480  // set all hits used so that we can use DefineHitPos. Note that
481  // the hit InTraj is not used or tested in DefineHitPos so this doesn't
482  // screw up any assns
483  if(!tp.Environment[kEnvDeadWire]) {
484  nMissed = 0;
485  tp.UseHit.set();
486  DefineHitPos(slc, tp);
487  fctj.TotChg += tp.Chg;
488  tp.Delta = PointTrajDOCA(slc, tp.HitPos[0], tp.HitPos[1], tp);
489  tp.DeltaRMS = tp.Delta / window;
490  tp.Environment.reset();
491  totHits += tp.Hits.size();
492  if(tp.Chg > maxChg) {
493  maxChg = tp.Chg;
494  maxChgPt = fctj.Pts.size();
495  }
496  // Note that ChgPull uses the average charge and charge RMS of the
497  // trajectory before it entered the forecast envelope
498  tp.ChgPull = (tp.Chg / minAveChg - 1) / tj.ChgRMS;
499  if((tp.ChgPull > 3 && tp.Hits.size() > 1) || tp.ChgPull > 10) {
500  ++nShLike;
501  // break if the tj was tracklike before this point
502  if(nShLike > minShPts && nTkLike >= minShPts) {
503  trimPts = minShPts;
504  showerStartNear = npwc + fctj.Pts.size() - minShPts;
505  if(doPrt) {
506  mf::LogVerbatim("TC")<<"showerlike was tracklike - break";
507  }
508  break;
509  }
510  // not track-like anymore
511  if(nShLike >= minShPts) nTkLike = 0;
512  // break if it approaches the side of the envelope
513  if(tp.DeltaRMS > 0.8) {
514  leavesNear = npwc + fctj.Pts.size();
515  leavesBeforeEnd = true;
516  break;
517  }
518  tp.Environment[kEnvNearShower] = true;
519  // flag a showerlike TP so it isn't used in the MCSMom calculation
520  tp.HitPosErr2 = 100;
521  } else {
522  ++nTkLike;
523  // break if the tj was shower-like before this point
524  if(nTkLike > minShPts && nShLike >= minShPts) {
525  trimPts = minShPts;
526  showerEndNear = npwc + fctj.Pts.size() - minShPts;
527  if(doPrt) {
528  mf::LogVerbatim("TC")<<"tracklike was showerlike - break";
529  }
530  break;
531  }
532  // not shower-like anymore
533  if(nTkLike >= minShPts) nShLike = 0;
534  // break if it approaches the side of the envelope
535  if(tp.DeltaRMS > 0.9) {
536  leavesNear = npwc + fctj.Pts.size();
537  leavesBeforeEnd = true;
538  break;
539  }
540  }
541  fctj.Pts.push_back(tp);
542  if(doPrt) {
543  mf::LogVerbatim myprt("TC");
544  myprt<<std::setw(4)<<npwc + fctj.Pts.size()<<" "<<PrintPos(slc, tp);
545  myprt<<std::setw(5)<<tp.Hits.size();
546  myprt<<std::setw(5)<<(int)tp.Chg;
547  myprt<<std::fixed<<std::setprecision(1);
548  myprt<<std::setw(8)<<tp.ChgPull;
549  myprt<<std::setw(8)<<tp.Delta;
550  myprt<<std::setw(8)<<std::setprecision(2)<<tp.DeltaRMS;
551  myprt<<std::setw(8)<<sqrt(tp.HitPosErr2);
552  myprt<<std::setw(6)<<nTkLike;
553  myprt<<std::setw(6)<<nShLike;
554  } // doPrt
555  } else {
556 // if(doPrt) std::cout<<istp<<" Pos "<<PrintPos(slc, tp)<<" window "<<window<<" dead wire\n";
557  }
558  } else {
559  // no hits found
560  ++nMissed;
561  if(nMissed == 10) {
562  if(doPrt) mf::LogVerbatim("TC")<<"No hits found after 10 steps - break";
563  break;
564  }
565  } // no hits found
566  } // istp
567  // not enuf info to make a forecast
568  tcc.dbgStp = doPrt;
569  if(fctj.Pts.size() < 6) return;
570  // truncate and re-calculate totChg?
571  if(trimPts > 0) {
572  // truncate the forecast trajectory
573  fctj.Pts.resize(fctj.Pts.size() - trimPts);
574  // recalculate the total charge
575  fctj.TotChg = 0;
576  for(auto& tp : fctj.Pts) fctj.TotChg += tp.Chg;
577  } // showerEndNear != USHRT_MAX
578  SetEndPoints(fctj);
579  fctj.MCSMom = MCSMom(slc, fctj);
580  tjf.MCSMom = fctj.MCSMom;
581  if(maxChgPt > 0.3 * fctj.Pts.size() && maxChg > 3 * tj.AveChg) {
582  // find the charge slope from the beginning to the maxChgPt if it is well away
583  // from the start and it is very large
584  ChgSlope(slc, fctj, fctj.EndPt[0], maxChgPt, tjf.chgSlope, tjf.chgSlopeErr, tjf.chgFitChiDOF);
585  } else {
586  ChgSlope(slc, fctj, tjf.chgSlope, tjf.chgSlopeErr, tjf.chgFitChiDOF);
587  }
588  ChkStop(slc, fctj);
589  UpdateTjChgProperties("Fc", slc, fctj, false);
590  tjf.chgRMS = fctj.ChgRMS;
591  tjf.endBraggPeak = fctj.StopFlag[1][kBragg];
592  // Set outlook = Estimate of the number of hits per wire
593  tjf.outlook = fctj.TotChg / (fctj.Pts.size() * tj.AveChg);
594  // assume we got to the end
595  tjf.nextForecastUpdate = npwc + fctj.Pts.size();
596  tjf.leavesBeforeEnd = leavesBeforeEnd;
597  tjf.foundShower = false;
598  if(leavesNear < tjf.nextForecastUpdate) {
599  // left the side
600  tjf.nextForecastUpdate = leavesNear;
601  } else if(showerStartNear < tjf.nextForecastUpdate) {
602  // found a shower start
603  tjf.nextForecastUpdate = showerStartNear;
604  tjf.foundShower = true;
605  } else if(showerEndNear < tjf.nextForecastUpdate) {
606  // found a shower end
607  tjf.nextForecastUpdate = showerEndNear;
608  }
609  nShLike = 0;
610  for(auto& tp : fctj.Pts) if(tp.Environment[kEnvNearShower]) ++nShLike;
611  tjf.showerLikeFraction = (float)nShLike / (float)fctj.Pts.size();
612 
613  if(doPrt) {
614  mf::LogVerbatim myprt("TC");
615  myprt<<"Forecast T"<<tj.ID<<" tj.AveChg "<<(int)tj.AveChg;
616  myprt<<" start "<<PrintPos(slc, tj.Pts[tj.EndPt[1]])<<" cnt "<<fctj.Pts.size()<<" totChg "<<(int)fctj.TotChg;
617  myprt<<" last pos "<<PrintPos(slc, tp);
618  myprt<<" MCSMom "<<tjf.MCSMom;
619  myprt<<" outlook "<<std::fixed<<std::setprecision(2)<<tjf.outlook;
620  myprt<<" chgSlope "<<std::setprecision(1)<<tjf.chgSlope<<" +/- "<<tjf.chgSlopeErr;
621  myprt<<" chgRMS "<<std::setprecision(1)<<tjf.chgRMS;
622  myprt<<" endBraggPeak "<<tjf.endBraggPeak;
623  myprt<<" chiDOF "<<tjf.chgFitChiDOF;
624  myprt<<" showerLike fraction "<<tjf.showerLikeFraction;
625  myprt<<" nextForecastUpdate "<<tjf.nextForecastUpdate;
626  myprt<<" leavesBeforeEnd? "<<tjf.leavesBeforeEnd;
627  myprt<<" foundShower? "<<tjf.foundShower;
628  }
629 
630  } // Forecast
631 
634  {
635  // A different stategy for updating a high energy electron trajectories
636  if(!tj.Strategy[kStiffEl]) return;
637  TrajPoint& lastTP = tj.Pts[tj.EndPt[1]];
638  // Set the lastPT delta before doing the fit
639  lastTP.Delta = PointTrajDOCA(slc, lastTP.HitPos[0], lastTP.HitPos[1], lastTP);
640  if(tj.Pts.size() < 30) lastTP.NTPsFit += 1;
641  FitTraj(slc, tj);
642  UpdateTjChgProperties("UET", slc, tj, tcc.dbgStp);
643  UpdateDeltaRMS(slc, tj);
644 // SetAngleCode(lastTP);
645  tj.MCSMom = MCSMom(slc, tj);
646  if(tcc.dbgStp) {
647  mf::LogVerbatim("TC")<<"UpdateStiffEl: lastPt "<<tj.EndPt[1]<<" Delta "<<lastTP.Delta<<" AngleCode "<<lastTP.AngleCode<<" FitChi "<<lastTP.FitChi<<" NTPsFit "<<lastTP.NTPsFit<<" MCSMom "<<tj.MCSMom;
648  }
649  tj.NeedsUpdate = false;
650  tj.PDGCode = 111;
651  return;
652  } // UpdateStiffTj
653 
655  void UpdateTraj(TCSlice& slc, Trajectory& tj)
656  {
657  // Updates the last added trajectory point fit, average hit rms, etc.
658 
659  tj.NeedsUpdate = true;
660  tj.MaskedLastTP = false;
661 
662  if(tj.EndPt[1] < 1) return;
663 
664  if(tj.Strategy[kStiffEl]) {
665  UpdateStiffEl(slc, tj);
666  return;
667  }
668  unsigned int lastPt = tj.EndPt[1];
669  TrajPoint& lastTP = tj.Pts[lastPt];
670  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
671 
672  // find the previous TP that has hits (and was therefore in the fit)
673  unsigned short prevPtWithHits = USHRT_MAX;
674  unsigned short firstFitPt = tj.EndPt[0];
675  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
676  unsigned short ipt = lastPt - ii;
677  if(tj.Pts[ipt].Chg > 0) {
678  prevPtWithHits = ipt;
679  break;
680  }
681  if(ipt == 0) break;
682  } // ii
683  if(prevPtWithHits == USHRT_MAX) return;
684 
685  // define the FitChi threshold above which something will be done
686  float maxChi = 2;
687  unsigned short minPtsFit = tcc.minPtsFit[tj.Pass];
688  // just starting out?
689  if(lastPt < 4) minPtsFit = 2;
690  bool cleanMuon = (tj.PDGCode == 13 && TrajIsClean(slc, tj, tcc.dbgStp) && !tj.Strategy[kSlowing]);
691  // was !TrajIsClean...
692  if(cleanMuon) {
693  // Fitting a clean muon
694  maxChi = tcc.maxChi;
695  minPtsFit = lastPt / 3;
696  }
697 
698  // Set the lastPT delta before doing the fit
699  lastTP.Delta = PointTrajDOCA(slc, lastTP.HitPos[0], lastTP.HitPos[1], lastTP);
700 
701  // update MCSMom. First ensure that nothing bad has happened
702  if(npwc > 3 && tj.Pts[lastPt].Chg > 0 && !tj.Strategy[kSlowing]) {
703  short newMCSMom = MCSMom(slc, tj);
704  short minMCSMom = 0.6 * tj.MCSMom;
705  if(tcc.useAlg[kNewStpCuts]) minMCSMom = 0.5 * tj.MCSMom;
706  if(lastPt > 10 && newMCSMom < minMCSMom) {
707  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"UpdateTraj: MCSMom took a nose-dive "<<newMCSMom;
708  UnsetUsedHits(slc, lastTP);
709  DefineHitPos(slc, lastTP);
710  SetEndPoints(tj);
711  tj.NeedsUpdate = false;
712  return;
713  }
714  tj.MCSMom = newMCSMom;
715  } // npwc > 3
716 
717 
718  if(tcc.dbgStp) {
719  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;
720  }
721 
722  UpdateTjChgProperties("UT", slc, tj, tcc.dbgStp);
723 
724  if(lastPt == 1) {
725  // Handle the second trajectory point. No error calculation. Just update
726  // the position and direction
727  lastTP.NTPsFit = 2;
728  FitTraj(slc, tj);
729  lastTP.FitChi = 0.01;
730  lastTP.AngErr = tj.Pts[0].AngErr;
731  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"UpdateTraj: Second traj point pos "<<lastTP.Pos[0]<<" "<<lastTP.Pos[1]<<" dir "<<lastTP.Dir[0]<<" "<<lastTP.Dir[1];
732  tj.NeedsUpdate = false;
733  SetAngleCode(lastTP);
734  return;
735  }
736 
737  if(lastPt == 2) {
738  // Third trajectory point. Keep it simple
739  lastTP.NTPsFit = 3;
740  FitTraj(slc, tj);
741  tj.NeedsUpdate = false;
742  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"UpdateTraj: Third traj point fit "<<lastTP.FitChi;
743  SetAngleCode(lastTP);
744  return;
745  }
746 
747  // Fit with > 2 TPs
748  // Keep adding hits until Chi/DOF exceeds 1
749  if(tj.Pts[prevPtWithHits].FitChi < 1 && !tj.Strategy[kSlowing]) lastTP.NTPsFit += 1;
750  // Reduce the number of points fit if the trajectory is long and chisq is getting a bit larger
751  if(lastPt > 20 && tj.Pts[prevPtWithHits].FitChi > 1.5 && lastTP.NTPsFit > minPtsFit) lastTP.NTPsFit -= 2;
752  // don't let long muon fits get too long
753  if(tcc.useAlg[kNewStpCuts] && cleanMuon && lastPt > 200 && tj.Pts[prevPtWithHits].FitChi > 1.0) lastTP.NTPsFit -= 2;
754 
755  FitTraj(slc, tj);
756 
757  // don't get too fancy when we are starting out
758  if(lastPt < 6) {
759  tj.NeedsUpdate = false;
760  UpdateDeltaRMS(slc, tj);
761  SetAngleCode(lastTP);
762  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Return with lastTP.FitChi "<<lastTP.FitChi<<" Chg "<<lastTP.Chg;
763  return;
764  }
765 
766  // find the first point that was fit.
767  unsigned short cnt = 0;
768  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
769  unsigned short ipt = lastPt - ii;
770  if(tj.Pts[ipt].Chg > 0) {
771  firstFitPt = ipt;
772  ++cnt;
773  }
774  if(cnt == lastTP.NTPsFit) break;
775  if(ipt == 0) break;
776  }
777 
778  unsigned short ndead = DeadWireCount(slc, lastTP.HitPos[0], tj.Pts[firstFitPt].HitPos[0], tj.CTP);
779  if(lastTP.FitChi > 1.5 && tj.Pts.size() > 6) {
780  // A large chisq jump can occur if we just jumped a large block of dead wires. In
781  // this case we don't want to mask off the last TP but reduce the number of fitted points
782  // This count will be off if there a lot of dead or missing wires...
783  // reduce the number of points significantly
784  if(ndead > 5 && !cleanMuon) {
785  if(lastTP.NTPsFit > 5) lastTP.NTPsFit = 5;
786  } else {
787  // Have a longish trajectory and chisq was a bit large.
788  // Was this a sudden occurrence and the fraction of TPs are included
789  // in the fit? If so, we should mask off this
790  // TP and keep going. If these conditions aren't met, we
791  // should reduce the number of fitted points
792  float chirat = 0;
793  if(prevPtWithHits != USHRT_MAX && tj.Pts[prevPtWithHits].FitChi > 0) chirat = lastTP.FitChi / tj.Pts[prevPtWithHits].FitChi;
794  // Don't mask hits when doing RevProp. Reduce NTPSFit instead
795  tj.MaskedLastTP = (chirat > 1.5 && lastTP.NTPsFit > 0.3 * NumPtsWithCharge(slc, tj, false) && !tj.AlgMod[kRvPrp]);
796  // BB April 19, 2018: Don't mask TPs on low MCSMom Tjs
797  if(tj.MaskedLastTP && tj.MCSMom < 30) tj.MaskedLastTP = false;
798  if(tcc.dbgStp) {
799  mf::LogVerbatim("TC")<<" First fit chisq too large "<<lastTP.FitChi<<" prevPtWithHits chisq "<<tj.Pts[prevPtWithHits].FitChi<<" chirat "<<chirat<<" NumPtsWithCharge "<<NumPtsWithCharge(slc, tj, false)<<" tj.MaskedLastTP "<<tj.MaskedLastTP;
800  }
801  // we should also mask off the last TP if there aren't enough hits
802  // to satisfy the minPtsFit constraint
803  if(!tj.MaskedLastTP && NumPtsWithCharge(slc, tj, true) < minPtsFit) tj.MaskedLastTP = true;
804  } // few dead wires
805  } // lastTP.FitChi > 2 ...
806 
807  // Deal with a really long trajectory that is in trouble (uB cosmic).
808  if(tj.PDGCode == 13 && lastTP.FitChi > tcc.maxChi) {
809  if(lastTP.NTPsFit > 1.3 * tcc.muonTag[0]) {
810  lastTP.NTPsFit *= 0.8;
811  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Muon - Reduce NTPsFit "<<lastPt;
812  } else {
813  tj.MaskedLastTP = true;
814  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Muon - mask last point "<<lastPt;
815  }
816  }
817 
818  if(tcc.dbgStp) 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<<" tj.MaskedLastTP "<<tj.MaskedLastTP;
819  if(tj.MaskedLastTP) {
820  UnsetUsedHits(slc, lastTP);
821  DefineHitPos(slc, lastTP);
822  SetEndPoints(tj);
823  lastPt = tj.EndPt[1];
824  lastTP.NTPsFit -= 1;
825  FitTraj(slc, tj);
826  UpdateTjChgProperties("UT", slc, tj, tcc.dbgStp);
827  SetAngleCode(lastTP);
828  return;
829  } else {
830  // a more gradual change in chisq. Maybe reduce the number of points
831  unsigned short newNTPSFit = lastTP.NTPsFit;
832  // reduce the number of points fit to keep Chisq/DOF < 2 adhering to the pass constraint
833  // and also a minimum number of points fit requirement for long muons
834  float prevChi = lastTP.FitChi;
835  unsigned short ntry = 0;
836  float chiCut = 1.5;
837  while(lastTP.FitChi > chiCut && lastTP.NTPsFit > minPtsFit) {
838  if(lastTP.NTPsFit > 15) {
839  newNTPSFit = 0.7 * newNTPSFit;
840  } else if(lastTP.NTPsFit > 4) {
841  newNTPSFit -= 2;
842  } else {
843  newNTPSFit -= 1;
844  }
845  if(lastTP.NTPsFit < 3) newNTPSFit = 2;
846  if(newNTPSFit < minPtsFit) newNTPSFit = minPtsFit;
847  lastTP.NTPsFit = newNTPSFit;
848  // BB April 19: try to add a last lonely hit on a low MCSMom tj on the last try
849  if(newNTPSFit == minPtsFit && tj.MCSMom < 30) chiCut = 2;
850  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Bad FitChi "<<lastTP.FitChi<<" Reduced NTPsFit to "<<lastTP.NTPsFit<<" Pass "<<tj.Pass<<" chiCut "<<chiCut;
851  FitTraj(slc, tj);
852  tj.NeedsUpdate = true;
853  if(lastTP.FitChi > prevChi) {
854  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Chisq is increasing "<<lastTP.FitChi<<" Try to remove an earlier bad hit";
855  MaskBadTPs(slc, tj, chiCut);
856  ++ntry;
857  if(ntry == 2) break;
858  }
859  prevChi = lastTP.FitChi;
860  if(lastTP.NTPsFit == minPtsFit) break;
861  } // lastTP.FitChi > 2 && lastTP.NTPsFit > 2
862  }
863  // last ditch attempt if things look bad. Drop the last hit
864  if(tj.Pts.size() > tcc.minPtsFit[tj.Pass] && lastTP.FitChi > maxChi) {
865  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Last try. Drop last TP "<<lastTP.FitChi<<" NTPsFit "<<lastTP.NTPsFit;
866  UnsetUsedHits(slc, lastTP);
867  DefineHitPos(slc, lastTP);
868  SetEndPoints(tj);
869  lastPt = tj.EndPt[1];
870  FitTraj(slc, tj);
871  tj.MaskedLastTP = true;
872  }
873 
874  if(tj.NeedsUpdate) UpdateTjChgProperties("UT", slc, tj, tcc.dbgStp);
875 
876  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Fit done. Chi "<<lastTP.FitChi<<" NTPsFit "<<lastTP.NTPsFit;
877 
878  if(tj.EndPt[0] == tj.EndPt[1]) return;
879 
880  // Don't let the angle error get too small too soon. Stepping would stop if the first
881  // few hits on a low momentum wandering track happen to have a very good fit to a straight line.
882  // We will do this by averaging the default starting value of AngErr of the first TP with the current
883  // value from FitTraj.
884  if(lastPt < 14) {
885  float defFrac = 1 / (float)(tj.EndPt[1]);
886  lastTP.AngErr = defFrac * tj.Pts[0].AngErr + (1 - defFrac) * lastTP.AngErr;
887  }
888 
889  UpdateDeltaRMS(slc, tj);
890  SetAngleCode(lastTP);
891 
892  tj.NeedsUpdate = false;
893  return;
894 
895  } // UpdateTraj
896 
899  {
900  if(!tj.Strategy[kStiffEl]) return;
901  if(tcc.dbgStp) {
902  mf::LogVerbatim("TC")<<"inside CheckStiffTj with NumPtsWithCharge = "<<NumPtsWithCharge(slc, tj, false);
903  }
904  // Fill in any gaps with hits that were skipped, most likely delta rays on muon tracks
905  FillGaps(slc, tj);
906  // Update the trajectory parameters at the beginning of the trajectory
907  FixTrajBegin(slc, tj);
908  } // CheckStiffTj
909 
911  void CheckTraj(TCSlice& slc, Trajectory& tj)
912  {
913  // Check the quality of the trajectory and possibly trim it or flag it for deletion
914 
915  if(!tj.IsGood) return;
916 
917  // ensure that the end points are defined
918  SetEndPoints(tj);
919  if(tj.EndPt[0] == tj.EndPt[1]) return;
920 
921  if(tj.Strategy[kStiffEl]) {
922  CheckStiffEl(slc, tj);
923  return;
924  }
925 
926  if(tcc.dbgStp) {
927  mf::LogVerbatim("TC")<<"inside CheckTraj with NumPtsWithCharge = "<<NumPtsWithCharge(slc, tj, false);
928  }
929 
930  if(NumPtsWithCharge(slc, tj, false) < tcc.minPts[tj.Pass]) {
931  tj.IsGood = false;
932  return;
933  }
934 
935  // Look for a charge asymmetry between points on both sides of a high-
936  // charge point and trim points in the vicinity
937  ChkChgAsymmetry(slc, tj, tcc.dbgStp);
938 
939  // flag this tj as a junk Tj (even though it wasn't created in FindJunkTraj).
940  // Drop it and let FindJunkTraj do it's job
941  TagJunkTj(slc, tj, tcc.dbgStp);
942  if(tj.AlgMod[kJunkTj]) {
943  tj.IsGood = false;
944  return;
945  }
946 
947  tj.MCSMom = MCSMom(slc, tj);
948 
949  // See if the points at the stopping end can be included in the Tj
950  ChkStopEndPts(slc, tj, tcc.dbgStp);
951 
952  // remove any points at the end that don't have charge
953  tj.Pts.resize(tj.EndPt[1] + 1);
954 
955  // Ensure that a hit only appears once in the TJ
956  if(HasDuplicateHits(slc, tj, tcc.dbgStp)) {
957  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" HasDuplicateHits ";
958  tj.IsGood = false;
959  return;
960  }
961 
962  // See if this is a ghost trajectory
963  if(IsGhost(slc, tj)) {
964  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" CT: Ghost trajectory - trimmed hits ";
965  if(!tj.IsGood) return;
966  }
967 
968  if(tj.AlgMod[kJunkTj]) return;
969 
970  // checks are different for Very Large Angle trajectories
971  bool isVLA = (tj.Pts[tj.EndPt[1]].AngleCode == 2);
972  // The last two ranges are Large Angle and Very Large Angle. Determine if the TJ is Small Angle
973  bool isSA = (tj.Pts[tj.EndPt[1]].AngleCode == 0);
974 
975  // First remove any TPs at the end that have no hits after
976  // setting the StopFlag. Assume that there are no hits on TPs after the end
977  tj.StopFlag[1][kSignal] = false;
978  if(tj.EndPt[1] < tj.Pts.size() - 1) {
979  // There must be hits at the end so set the kSignal StopFlag
980  if(!tj.Pts[tj.EndPt[1]+1].Hits.empty()) tj.StopFlag[1][kSignal] = true;
981  }
982  tj.Pts.resize(tj.EndPt[1] + 1);
983 
984  // Fill in any gaps with hits that were skipped, most likely delta rays on muon tracks
985  if(!isVLA) FillGaps(slc, tj);
986 
987  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" CheckTraj MCSMom "<<tj.MCSMom<<" isVLA? "<<isVLA<<" NumPtsWithCharge "<<NumPtsWithCharge(slc, tj, false)<<" Min Req'd "<<tcc.minPts[tj.Pass];
988 
989  // Check for hit width consistency on short trajectories
990  if(tj.Pts.size() < 10) {
991  float maxWidth = 0;
992  float minWidth = 999;
993  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
994  if(tj.Pts[ipt].Chg == 0) continue;
995  if(tj.Pts[ipt].HitPosErr2 > maxWidth) maxWidth = tj.Pts[ipt].HitPosErr2;
996  if(tj.Pts[ipt].HitPosErr2 < minWidth) minWidth = tj.Pts[ipt].HitPosErr2;
997  } // ipt
998  // Require less than a 3X difference in the hit width or 10X for HitPosErr2
999  if(maxWidth > 10 * minWidth) {
1000  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" TP width variation too large: minWidth "<<minWidth<<" maxWidth "<<maxWidth;
1001  tj.IsGood = false;
1002  return;
1003  }
1004  } // short trajectory
1005 
1006  // Trim the end points until the TJ meets the quality cuts
1007  TrimEndPts("CT", slc, tj, tcc.qualityCuts, tcc.dbgStp);
1008  if(tj.AlgMod[kKilled]) {
1009  tj.IsGood = false;
1010  return;
1011  }
1012 
1013  // Check for a Bragg peak at both ends. This may be used by FixTrajBegin.
1014  ChkStop(slc, tj);
1015 
1016  // Update the trajectory parameters at the beginning of the trajectory
1017  FixTrajBegin(slc, tj);
1018 
1019  // ignore short trajectories
1020  if(tj.EndPt[1] < 4) return;
1021 
1022  if(isSA && !tj.StopFlag[1][kBragg]) {
1023  // Small angle checks
1024 
1025  if(tcc.useAlg[kCTKink] && tj.EndPt[1] > 8 && !tj.StopFlag[1][kAtKink] && tj.MCSMom > 50) {
1026  // look for the signature of a kink near the end of the trajectory.
1027  // These are: Increasing delta for the last few hits
1028  unsigned short newSize = USHRT_MAX;
1029  unsigned short lastPtToChk = tj.EndPt[1] - 4;
1030  float deltaCut = 2 * tj.Pts[lastPtToChk].DeltaRMS;
1031  for(unsigned short ipt = tj.EndPt[1]; ipt > lastPtToChk; --ipt) {
1032  // Stop checking if delta is good
1033  if(tj.Pts[ipt].Delta < deltaCut) break;
1034  float drat = tj.Pts[ipt].Delta / tj.Pts[ipt-1].Delta;
1035  if(drat > 1.2) newSize = ipt;
1036  } // ipt
1037  if(newSize != USHRT_MAX) {
1038  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"CTKink: Masking end points to newSize "<<newSize;
1039  for(unsigned short ipt = newSize; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
1040  SetEndPoints(tj);
1041  tj.AlgMod[kCTKink] = true;
1042  }
1043  } // tcc.useAlg[kCTKink]
1044 
1045  if(tcc.useAlg[kCTStepChk] && !tj.AlgMod[kRvPrp]) {
1046  // Compare the number of steps taken per TP near the beginning and
1047  // at the end. This will get confused if RevProp is used
1048  short nStepBegin = tj.Pts[2].Step - tj.Pts[1].Step;
1049  short nStepEnd;
1050  unsigned short lastPt = tj.Pts.size() - 1;
1051  unsigned short newSize = tj.Pts.size();
1052  for(unsigned short ipt = lastPt; ipt > lastPt - 2; --ipt) {
1053  nStepEnd = tj.Pts[ipt].Step - tj.Pts[ipt - 1].Step;
1054  if(nStepEnd > 3 * nStepBegin) newSize = ipt;
1055  }
1056  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"CTStepChk: check number of steps. newSize "<<newSize<<" tj.Pts.size() "<<tj.Pts.size();
1057  if(newSize < tj.Pts.size()) {
1058  for(unsigned short ipt = newSize; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
1059  SetEndPoints(tj);
1060  tj.AlgMod[kCTStepChk] = true;
1061  tj.Pts.resize(newSize);
1062  return;
1063  } // newSize < tj.Pts.size()
1064  } // tcc.useAlg[kCTStepChk]
1065  } // isSA
1066  // Oct 30, 2018. FindSoftKink needs work
1067 // FindSoftKink(slc, tj);
1068 
1069  HiEndDelta(slc, tj);
1070 
1071  // final quality check
1072  float npwc = NumPtsWithCharge(slc, tj, true);
1073  float npts = tj.EndPt[1] - tj.EndPt[0] + 1;
1074  float frac = npwc / npts;
1075  tj.IsGood = (frac >= tcc.qualityCuts[0]);
1076  if(tj.IsGood && tj.Pass < tcc.minMCSMom.size() && !tj.Strategy[kSlowing]) tj.IsGood = (tj.MCSMom >= tcc.minMCSMom[tj.Pass]);
1077  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"CTStepChk: fraction of points with charge "<<frac<<" good traj? "<<tj.IsGood;
1078  if(!tj.IsGood || !slc.isValid) return;
1079 
1080  // lop off high multiplicity hits at the end
1081  CheckHiMultEndHits(slc, tj);
1082 
1083  // Check for a Bragg peak at both ends. This may be used by FixTrajBegin.
1084  ChkStop(slc, tj);
1085 
1086  if(tcc.dbgStp && tj.Pts.size() < 100) PrintTrajectory("CTo", slc, tj, USHRT_MAX);
1087 
1088  } // CheckTraj
1089 
1091  void AddHits(TCSlice& slc, Trajectory& tj, unsigned short ipt, bool& sigOK)
1092  {
1093  // Try to add hits to the trajectory point ipt on the supplied
1094  // trajectory
1095 
1096  // assume failure
1097  sigOK = false;
1098 
1099  if(tj.Pts.empty()) return;
1100  if(ipt > tj.Pts.size() - 1) return;
1101 
1102  // Call large angle hit finding if the last tp is large angle
1103  if(tj.Pts[ipt].AngleCode == 2) {
1104  AddLAHits(slc, tj, ipt, sigOK);
1105  return;
1106  }
1107 
1108  TrajPoint& tp = tj.Pts[ipt];
1109  std::vector<unsigned int> closeHits;
1110  unsigned int lastPtWithUsedHits = tj.EndPt[1];
1111 
1112  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1113  unsigned int wire = std::nearbyint(tp.Pos[0]);
1114  if(wire < slc.firstWire[plane] || wire > slc.lastWire[plane]-1) return;
1115  // Move the TP to this wire
1116  MoveTPToWire(tp, (float)wire);
1117 
1118  // find the projection error to this point. Note that if this is the first
1119  // TP, lastPtWithUsedHits = 0, so the projection error is 0
1120  float dw = tp.Pos[0] - tj.Pts[lastPtWithUsedHits].Pos[0];
1121  float dt = tp.Pos[1] - tj.Pts[lastPtWithUsedHits].Pos[1];
1122  float dpos = sqrt(dw * dw + dt * dt);
1123  float projErr = dpos * tj.Pts[lastPtWithUsedHits].AngErr;
1124  // Add this to the Delta RMS factor and construct a cut
1125  float deltaCut = 3 * (projErr + tp.DeltaRMS);
1126 
1127  if(tcc.useAlg[kNewStpCuts]) {
1128  // The delta cut shouldn't be less than the delta of hits added on the previous step
1129  float minDeltaCut = 1.1 * tj.Pts[lastPtWithUsedHits].Delta;
1130  if(deltaCut < minDeltaCut) deltaCut = minDeltaCut;
1131  }
1132 
1133  deltaCut *= tcc.projectionErrFactor;
1134  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" AddHits: calculated deltaCut "<<deltaCut<<" dw "<<dw<<" dpos "<<dpos;
1135 
1136  if(deltaCut < 0.5) deltaCut = 0.5;
1137  if(deltaCut > 3) deltaCut = 3;
1138 
1139  // TY: open it up for RevProp, since we might be following a stopping track
1140  if(tj.AlgMod[kRvPrp]) deltaCut *= 2;
1141 
1142  // loosen up a bit if we just passed a block of dead wires
1143  bool passedDeadWires = (abs(dw) > 20 && DeadWireCount(slc, tp.Pos[0], tj.Pts[lastPtWithUsedHits].Pos[0], tj.CTP) > 10);
1144  if(passedDeadWires) deltaCut *= 2;
1145  // open it up for StiffEl and Slowing strategies
1146  if(tj.Strategy[kStiffEl] || tj.Strategy[kSlowing]) deltaCut = 2.5;
1147 
1148  // Create a larger cut to use in case there is nothing close
1149  float bigDelta = 2 * deltaCut;
1150  unsigned int imBig = UINT_MAX;
1151  tp.Delta = deltaCut;
1152  // ignore all hits with delta larger than maxDeltaCut
1153  float maxDeltaCut = 2 * bigDelta;
1154  // apply some limits
1155  if(tcc.useAlg[kNewStpCuts] && !passedDeadWires && maxDeltaCut > 3) {
1156  maxDeltaCut = 3;
1157  bigDelta = 1.5;
1158  }
1159 
1160  // projected time in ticks for testing the existence of a hit signal
1161  raw::TDCtick_t rawProjTick = (float)(tp.Pos[1] / tcc.unitsPerTick);
1162  if(tcc.dbgStp) {
1163  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(slc, tp);
1164  }
1165 
1166  std::vector<unsigned int> hitsInMultiplet;
1167 
1168  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1169  unsigned int ipl = planeID.Plane;
1170  if(wire > slc.lastWire[ipl]) return;
1171  // Assume a signal exists on a dead wire
1172  if(slc.wireHitRange[ipl][wire].first == -1) sigOK = true;
1173  if(slc.wireHitRange[ipl][wire].first < 0) return;
1174  unsigned int firstHit = (unsigned int)slc.wireHitRange[ipl][wire].first;
1175  unsigned int lastHit = (unsigned int)slc.wireHitRange[ipl][wire].second;
1176  float fwire = wire;
1177  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
1178  if(slc.slHits[iht].InTraj == tj.ID) continue;
1179  if(slc.slHits[iht].InTraj == SHRT_MAX) continue;
1180  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1181  if(rawProjTick > hit.StartTick() && rawProjTick < hit.EndTick()) sigOK = true;
1182  float ftime = tcc.unitsPerTick * hit.PeakTime();
1183  float delta = PointTrajDOCA(slc, fwire, ftime, tp);
1184  // increase the delta cut if this is a long pulse hit
1185  bool longPulseHit = LongPulseHit(hit);
1186  if(tcc.useAlg[kNewStpCuts] && longPulseHit) {
1187  if(delta > 3) continue;
1188  } else {
1189  if(delta > maxDeltaCut) continue;
1190  }
1191  float dt = std::abs(ftime - tp.Pos[1]);
1192  unsigned short localIndex = 0;
1193  GetHitMultiplet(slc, iht, hitsInMultiplet, localIndex);
1194  if(tcc.dbgStp && delta < 100 && dt < 100) {
1195  mf::LogVerbatim myprt("TC");
1196  myprt<<" iht "<<iht;
1197  myprt<<" "<<PrintHit(slc.slHits[iht]);
1198  myprt<<" delta "<<std::fixed<<std::setprecision(2)<<delta<<" deltaCut "<<deltaCut<<" dt "<<dt;
1199  myprt<<" BB Mult "<<hitsInMultiplet.size()<<" localIndex "<<localIndex<<" RMS "<<std::setprecision(1)<<hit.RMS();
1200  myprt<<" Chi "<<std::setprecision(1)<<hit.GoodnessOfFit();
1201  myprt<<" InTraj "<<slc.slHits[iht].InTraj;
1202  myprt<<" Chg "<<(int)hit.Integral();
1203  myprt<<" Signal? "<<sigOK;
1204  }
1205  if(slc.slHits[iht].InTraj == 0 && delta < bigDelta && hitsInMultiplet.size() < 3 && !tj.AlgMod[kRvPrp]) {
1206  // An available hit that is just outside the window that is not part of a large multiplet
1207  bigDelta = delta;
1208  imBig = iht;
1209  }
1210  if(tcc.useAlg[kNewStpCuts] && longPulseHit) {
1211  if(delta > 3) continue;
1212  } else {
1213  if(delta > deltaCut) continue;
1214  }
1215 
1216  if(std::find(closeHits.begin(), closeHits.end(), iht) != closeHits.end()) continue;
1217  closeHits.push_back(iht);
1218  if(hitsInMultiplet.size() > 1) {
1219  // include all the hits in a multiplet
1220  for(auto& jht : hitsInMultiplet) {
1221  if(slc.slHits[jht].InTraj == tj.ID) continue;
1222  if(std::find(closeHits.begin(), closeHits.end(), jht) != closeHits.end()) continue;
1223  closeHits.push_back(jht);
1224  } // jht
1225  } // multiplicity > 1
1226  } // iht
1227 
1228  if(tcc.dbgStp) {
1229  mf::LogVerbatim myprt("TC");
1230  myprt<<"closeHits ";
1231  for(auto iht : closeHits) myprt<<" "<<PrintHit(slc.slHits[iht]);
1232  if(imBig < slc.slHits.size()) {
1233  myprt<<" imBig "<<PrintHit(slc.slHits[imBig]);
1234  } else {
1235  myprt<<" imBig "<<imBig;
1236  }
1237  }
1238  if(closeHits.empty() && imBig == UINT_MAX) {
1239  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" no signal on any wire at tp.Pos "<<tp.Pos[0]<<" "<<tp.Pos[1]<<" tick "<<(int)tp.Pos[1]/tcc.unitsPerTick<<" closeHits size "<<closeHits.size();
1240  return;
1241  }
1242  if(imBig < slc.slHits.size() && closeHits.empty()) {
1243  closeHits.push_back(imBig);
1244  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Added bigDelta hit "<<PrintHit(slc.slHits[imBig])<<" w delta = "<<bigDelta;
1245  }
1246  if(!closeHits.empty()) sigOK = true;
1247  if(!sigOK) return;
1248  if(closeHits.size() > 16) closeHits.resize(16);
1249  tp.Hits.insert(tp.Hits.end(), closeHits.begin(), closeHits.end());
1250 
1251  // reset UseHit and assume that none of these hits will be used (yet)
1252  tp.UseHit.reset();
1253  if(tcc.useAlg[kStopAtTj]) {
1254  // don't continue if we have run into another trajectory and there are no available hits
1255  unsigned short nAvailable = 0;
1256  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1257  auto& tcHit = slc.slHits[tp.Hits[ii]];
1258  if(tcHit.InTraj == 0) ++nAvailable;
1259  } // ii
1260  if(nAvailable == 0) {
1261  tj.StopFlag[1][kAtTj] = true;
1262  return;
1263  }
1264  } // stop at Tj
1265  // decide which of these hits should be used in the fit. Use a generous maximum delta
1266  // and require a charge check if we'not just starting out
1267  bool useChg = true;
1268  if(tj.Strategy[kStiffEl] || tj.Strategy[kSlowing]) useChg = false;
1269  FindUseHits(slc, tj, ipt, 10, useChg);
1270  DefineHitPos(slc, tp);
1271  SetEndPoints(tj);
1272  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" number of close hits "<<closeHits.size()<<" used hits "<<NumHitsInTP(tp, kUsedHits);
1273  } // AddHits
1274 
1275 
1277  void AddLAHits(TCSlice& slc, Trajectory& tj, unsigned short ipt, bool& sigOK)
1278  {
1279  // Very Large Angle version of AddHits to be called for the last angle range
1280 
1281  if(ipt > tj.Pts.size() - 1) return;
1282  TrajPoint& tp = tj.Pts[ipt];
1283  tp.Hits.clear();
1284  tp.UseHit.reset();
1285  sigOK = false;
1286 
1287  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1288 
1289  // look at adjacent wires for larger angle trajectories
1290  // We will check the most likely wire first
1291  std::vector<int> wires(1);
1292  wires[0] = std::nearbyint(tp.Pos[0]);
1293  if(wires[0] < 0 || wires[0] > (int)slc.lastWire[plane]-1) return;
1294 
1295  if(tp.AngleCode != 2) {
1296  mf::LogVerbatim("TC")<<"AddLAHits called with a bad angle code. "<<tp.AngleCode<<" Don't do this";
1297  return;
1298  }
1299  // and the adjacent wires next in the most likely order only
1300  // after the first two points have been defined
1301  if(ipt > 1) {
1302  if(tp.Dir[0] > 0) {
1303  if(wires[0] < (int)slc.lastWire[plane]-1) wires.push_back(wires[0] + 1);
1304  if(wires[0] > 0) wires.push_back(wires[0] - 1);
1305  } else {
1306  if(wires[0] > 0) wires.push_back(wires[0] - 1);
1307  if(wires[0] < (int)slc.lastWire[plane]-1) wires.push_back(wires[0] + 1);
1308  }
1309  } // ipt > 0 ...
1310 
1311  if(tcc.dbgStp) {
1312  mf::LogVerbatim myprt("TC");
1313  myprt<<" AddLAHits: Pos "<<PrintPos(slc, tp)<<" tp.AngleCode "<<tp.AngleCode<<" Wires under consideration";
1314  for(auto& wire : wires) myprt<<" "<<wire;
1315  }
1316 
1317  // a temporary tp that we can move around
1318  TrajPoint ltp = tp;
1319  // do this while testing
1320  sigOK = false;
1321 
1322  tp.Hits.clear();
1323  std::array<int, 2> wireWindow;
1324  std::array<float, 2> timeWindow;
1325  float pos1Window = tcc.VLAStepSize/2;
1326  timeWindow[0] = ltp.Pos[1] - pos1Window;
1327  timeWindow[1] = ltp.Pos[1] + pos1Window;
1328  // Put the existing hits in to a vector so we can ensure that they aren't added again
1329  std::vector<unsigned int> oldHits = PutTrajHitsInVector(tj, kAllHits);
1330 
1331  for(unsigned short ii = 0; ii < wires.size(); ++ii) {
1332  int wire = wires[ii];
1333  if(wire < 0 || wire > (int)slc.lastWire[plane]) continue;
1334  // Assume a signal exists on a dead wire
1335  if(slc.wireHitRange[plane][wire].first == -1) sigOK = true;
1336  if(slc.wireHitRange[plane][wire].first < 0) continue;
1337  wireWindow[0] = wire;
1338  wireWindow[1] = wire;
1339  bool hitsNear;
1340  // Look for hits using the requirement that the timeWindow overlaps with the hit StartTick and EndTick
1341  std::vector<unsigned int> closeHits = FindCloseHits(slc, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
1342  if(hitsNear) sigOK = true;
1343  for(auto& iht : closeHits) {
1344  // Ensure that none of these hits are already used by this trajectory
1345  if(slc.slHits[iht].InTraj == tj.ID) continue;
1346  // or in another trajectory in any previously added point
1347  if(std::find(oldHits.begin(), oldHits.end(), iht) != oldHits.end()) continue;
1348  tp.Hits.push_back(iht);
1349  }
1350  } // ii
1351 
1352  if(tcc.dbgStp) {
1353  mf::LogVerbatim myprt("TC");
1354  myprt<<" LAPos "<<PrintPos(slc, ltp)<<" Tick window "<<(int)(timeWindow[0]/tcc.unitsPerTick)<<" to "<<(int)(timeWindow[1]/tcc.unitsPerTick);
1355  for(auto& iht : tp.Hits) myprt<<" "<<PrintHit(slc.slHits[iht]);
1356  } // prt
1357 
1358  // no hits found
1359  if(tp.Hits.empty()) return;
1360 
1361  if(tp.Hits.size() > 16) tp.Hits.resize(16);
1362 
1363  tp.UseHit.reset();
1364 
1365  if(tcc.useAlg[kStopAtTj]) {
1366  // don't continue if we have run into another trajectory that has a similar angle
1367  unsigned short nAvailable = 0;
1368  unsigned int otherTjHit = INT_MAX;
1369  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1370  auto& tcHit = slc.slHits[tp.Hits[ii]];
1371  if(tcHit.InTraj == SHRT_MAX) continue;
1372  if(tcHit.InTraj > 0) {
1373  otherTjHit = tp.Hits[ii];
1374  continue;
1375  }
1376  ++nAvailable;
1377  } // ii
1378  if(nAvailable == 0 && otherTjHit != UINT_MAX) {
1379  // get the trajectory index
1380  unsigned short otherTj = slc.slHits[otherTjHit].InTraj - 1;
1381  Trajectory& otj = slc.tjs[otherTj];
1382  // find out what point the hit is in
1383  unsigned short atPt = USHRT_MAX;
1384  for(unsigned short ipt = 0; ipt < otj.Pts.size(); ++ipt) {
1385  for(auto& iht : otj.Pts[ipt].Hits) {
1386  if(iht == otherTjHit) {
1387  atPt = ipt;
1388  break;
1389  } // iht == otherTjHit
1390  } // iht
1391  if(atPt != USHRT_MAX) break;
1392  } // ipt
1393  if(atPt != USHRT_MAX && DeltaAngle(tp.Ang, otj.Pts[atPt].Ang) < 0.1) {
1394  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Found a VLA merge candidate trajectory "<<otj.ID<<". Set StopFlag[kAtTj] and stop stepping";
1395  tj.StopFlag[1][kAtTj] = true;
1396  return;
1397  } // atPt is valid
1398  } // nAvailable == 0 &&
1399  } // stop at Tj
1400 
1401  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1402  unsigned int iht = tp.Hits[ii];
1403  if(slc.slHits[iht].InTraj != 0) continue;
1404  tp.UseHit[ii] = true;
1405  slc.slHits[iht].InTraj = tj.ID;
1406  } // ii
1407  DefineHitPos(slc, tp);
1408  SetEndPoints(tj);
1409  UpdateTjChgProperties("ALAH", slc, tj, tcc.dbgStp);
1410 
1411  } // AddLAHits
1412 
1415  {
1416  // Reverse the trajectory and step in the opposite direction. The
1417  // updated trajectory is returned if this process is successful
1418 
1419  if(!tcc.useAlg[kRvPrp]) return;
1420 
1421  if(tj.Pts.size() < 6) return;
1422  // only do this once
1423  if(tj.AlgMod[kRvPrp]) return;
1424 
1425  // This code can't handle VLA trajectories
1426  if(tj.Pts[tj.EndPt[0]].AngleCode == 2) return;
1427 
1428  bool prt = (tcc.dbgStp || tcc.dbgAlg[kRvPrp]);
1429 
1430  if(tj.EndPt[0] > 0) {
1431  tj.Pts.erase(tj.Pts.begin(), tj.Pts.begin() + tj.EndPt[0]);
1432  SetEndPoints(tj);
1433  }
1434 
1435  if(prt) mf::LogVerbatim("TC")<<"ReversePropagate: Prepping Tj "<<tj.ID<<" incoming StepDir "<<tj.StepDir;
1436 
1437  short stepDir = tj.StepDir;
1438 
1439  // find the wire on which EndPt resides
1440  unsigned int wire0 = std::nearbyint(tj.Pts[0].Pos[0]);
1441  unsigned int nextWire = wire0 - tj.StepDir;
1442 
1443  // check for dead wires
1444  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1445  unsigned short ipl = planeID.Plane;
1446  while(nextWire > slc.firstWire[ipl] && nextWire < slc.lastWire[ipl]) {
1447  if(slc.wireHitRange[ipl][nextWire].first >= 0) break;
1448  nextWire -= tj.StepDir;
1449  }
1450  if(nextWire == slc.lastWire[ipl] - 1) return;
1451  // clone the first point
1452  TrajPoint tp = tj.Pts[0];
1453  // strip off the hits
1454  tp.Hits.clear(); tp.UseHit.reset();
1455  // move it to the next wire
1456  MoveTPToWire(tp, (float)nextWire);
1457  // find close unused hits near this position
1458  float maxDelta = 10 * tj.Pts[tj.EndPt[1]].DeltaRMS;
1459  if(!FindCloseHits(slc, tp, maxDelta, kUnusedHits)) return;
1460  if(prt) mf::LogVerbatim("TC")<<" nUnused hits "<<tp.Hits.size()<<" at Pos "<<PrintPos(slc, tp);
1461  if(tp.Hits.empty()) return;
1462  // There are hits on the next wire. Make a copy, reverse it and try
1463  // to extend it with StepAway
1464  if(prt) {
1465  mf::LogVerbatim myprt("TC");
1466  myprt<<" tp.Hits ";
1467  for(auto& iht : tp.Hits) myprt<<" "<<PrintHit(slc.slHits[iht])<<"_"<<slc.slHits[iht].InTraj;
1468  } // tcc.dbgStp
1469  //
1470  // Make a working copy of tj
1471  Trajectory tjWork = tj;
1472  // So the first shall be last and the last shall be first
1473  ReverseTraj(slc, tjWork);
1474  // Flag it to use special cuts in StepAway
1475  tjWork.AlgMod[kRvPrp] = true;
1476  // save the strategy word and set it to normal
1477  auto saveStrategy = tjWork.Strategy;
1478  tjWork.Strategy.reset();
1479  tjWork.Strategy[kNormal] = true;
1480  // Reduce the number of fitted points to a small number
1481  unsigned short lastPt = tjWork.Pts.size() - 1;
1482  if(lastPt < 4) return;
1483  // update the charge
1484  float chg = 0;
1485  float cnt = 0;
1486  for(unsigned short ii = 0; ii < 4; ++ii) {
1487  unsigned short ipt = lastPt - ii;
1488  if(tjWork.Pts[ipt].Chg == 0) continue;
1489  chg += tjWork.Pts[ipt].Chg;
1490  ++cnt;
1491  } // ii
1492  if(cnt == 0) return;
1493  if(cnt > 1) tjWork.Pts[lastPt].AveChg = chg / cnt;
1494  StepAway(slc, tjWork);
1495  if(!tj.IsGood) {
1496  if(prt) mf::LogVerbatim("TC")<<" ReversePropagate StepAway failed";
1497  return;
1498  }
1499  tjWork.Strategy = saveStrategy;
1500  // check the new stopping point
1501  ChkStopEndPts(slc, tjWork, tcc.dbgStp);
1502  // restore the original direction
1503  if(tjWork.StepDir != stepDir) ReverseTraj(slc, tjWork);
1504  tj = tjWork;
1505  // TODO: Maybe UpdateTjChgProperties should be called here
1506  // re-check the ends
1507  ChkStop(slc, tj);
1508  if(prt) {
1509  mf::LogVerbatim("TC")<<" ReversePropagate success. Outgoing StepDir "<<tj.StepDir;
1510 // if(tj.Pts.size() < 50) PrintTrajectory("RP", slc, tj, USHRT_MAX);
1511  }
1512 
1513  } // ReversePropagate
1514 
1515 /*
1517  void UseUnusedHits(TCSlice& slc)
1518  {
1519  if(slc.tjs.size() == 0) return;
1520  if(!tcc.useAlg[kUUH]) return;
1521 
1522  // max change in position allowed after adding all unused hits in a multiplet
1523  float maxPosSep2 = 0.25;
1524 
1525  std::vector<unsigned int> hitsInMultiplet;
1526  for(unsigned short itj = 0; itj < slc.tjs.size(); ++itj) {
1527  Trajectory& tj = slc.tjs[itj];
1528  if(tj.AlgMod[kKilled]) continue;
1529  // Find the max delta
1530  unsigned short firstPt = tj.EndPt[0];
1531  unsigned short lastPt = tj.EndPt[1];
1532  for(unsigned short ipt = firstPt; ipt <= lastPt; ++ipt) {
1533  TrajPoint& tp = tj.Pts[ipt];
1534  if(AngleRange(tp) == 0) continue;
1535  if(tp.Hits.empty()) continue;
1536  // ignore if the TP has long-pulse hits -> large HitPosErr2
1537  if(tp.HitPosErr2 > 50) continue;
1538  bool hitsAdded = false;
1539  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1540  if(!tp.UseHit[ii]) continue;
1541  unsigned int iht = tp.Hits[ii];
1542  GetHitMultiplet(slc, iht, hitsInMultiplet);
1543  if(hitsInMultiplet.size() > 1) {
1544  for(unsigned short jj = ii + 1; jj < tp.Hits.size(); ++jj) {
1545  if(!tp.UseHit[jj]) continue;
1546  if(std::find(hitsInMultiplet.begin(), hitsInMultiplet.end(), tp.Hits[jj]) != hitsInMultiplet.end()) {
1547  tp.UseHit[jj] = true;
1548  slc.slHits[tp.Hits[jj]].InTraj = tj.ID;
1549  hitsAdded = true;
1550  }
1551  } // jj
1552  }
1553  } // ii
1554  if(hitsAdded) {
1555  // save the hit position
1556  std::array<float, 2> oldHitPos = tp.HitPos;
1557  DefineHitPos(slc, tp);
1558  // keep it if
1559  if(PosSep2(tj.Pts[ipt].HitPos, oldHitPos) < maxPosSep2) {
1560  tj.AlgMod[kUUH] = true;
1561  } else {
1562  UnsetUsedHits(slc, tj.Pts[ipt]);
1563  }
1564  }
1565  } // ipt
1566  if(tj.AlgMod[kUUH]) SetEndPoints(tj);
1567  } // itj
1568 
1569  } // UseUnusedHits
1570 */
1571 
1573  void GetHitMultiplet(TCSlice& slc, unsigned int theHit, std::vector<unsigned int>& hitsInMultiplet)
1574  {
1575  unsigned short localIndex;
1576  GetHitMultiplet(slc, theHit, hitsInMultiplet, localIndex);
1577  } // GetHitMultiplet
1578 
1580  void GetHitMultiplet(TCSlice& slc, unsigned int theHit, std::vector<unsigned int>& hitsInMultiplet, unsigned short& localIndex)
1581  {
1582  hitsInMultiplet.clear();
1583  localIndex = 0;
1584  if(theHit > slc.slHits.size() - 1) return;
1585  if(slc.slHits[theHit].InTraj == SHRT_MAX) return;
1586  hitsInMultiplet.resize(1);
1587  hitsInMultiplet[0] = theHit;
1588 
1589  auto& hit = (*evt.allHits)[slc.slHits[theHit].allHitsIndex];
1590  unsigned int theWire = hit.WireID().Wire;
1591  unsigned short ipl = hit.WireID().Plane;
1592  float theTime = hit.PeakTime();
1593  float theRMS = hit.RMS();
1594  float narrowHitCut = 1.5 * evt.aveHitRMS[ipl];
1595  bool theHitIsNarrow = (theRMS < narrowHitCut);
1596  float maxPeak = hit.PeakAmplitude();
1597  unsigned short imTall = theHit;
1598  unsigned short nNarrow = 0;
1599  if(theHitIsNarrow) nNarrow = 1;
1600  // look for hits < theTime but within hitSep
1601  if(theHit > 0) {
1602  for(unsigned int iht = theHit - 1; iht != 0; --iht) {
1603  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1604  if(hit.WireID().Wire != theWire) break;
1605  if(hit.WireID().Plane != ipl) break;
1606  float hitSep = tcc.multHitSep * theRMS;
1607  float rms = hit.RMS();
1608  if(rms > theRMS) {
1609  hitSep = tcc.multHitSep * rms;
1610  theRMS = rms;
1611  }
1612  float dTick = std::abs(hit.PeakTime() - theTime);
1613  if(dTick > hitSep) break;
1614  hitsInMultiplet.push_back(iht);
1615  if(rms < narrowHitCut) ++nNarrow;
1616  float peakAmp = hit.PeakAmplitude();
1617  if(peakAmp > maxPeak) {
1618  maxPeak = peakAmp;
1619  imTall = iht;
1620  }
1621  theTime = hit.PeakTime();
1622  if(iht == 0) break;
1623  } // iht
1624  } // iht > 0
1625  localIndex = hitsInMultiplet.size() - 1;
1626  // reverse the order so that hitsInMuliplet will be
1627  // returned in increasing time order
1628  if(hitsInMultiplet.size() > 1) std::reverse(hitsInMultiplet.begin(), hitsInMultiplet.end());
1629  // look for hits > theTime but within hitSep
1630  theTime = hit.PeakTime();
1631  theRMS = hit.RMS();
1632  for(unsigned int iht = theHit + 1; iht < slc.slHits.size(); ++iht) {
1633  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1634  if(hit.WireID().Wire != theWire) break;
1635  if(hit.WireID().Plane != ipl) break;
1636  if(slc.slHits[iht].InTraj == SHRT_MAX) continue;
1637  float hitSep = tcc.multHitSep * theRMS;
1638  float rms = hit.RMS();
1639  if(rms > theRMS) {
1640  hitSep = tcc.multHitSep * rms;
1641  theRMS = rms;
1642  }
1643  float dTick = std::abs(hit.PeakTime() - theTime);
1644  if(dTick > hitSep) break;
1645  hitsInMultiplet.push_back(iht);
1646  if(rms < narrowHitCut) ++nNarrow;
1647  float peakAmp = hit.PeakAmplitude();
1648  if(peakAmp > maxPeak) {
1649  maxPeak = peakAmp;
1650  imTall = iht;
1651  }
1652  theTime = hit.PeakTime();
1653  } // iht
1654  if(hitsInMultiplet.size() == 1) return;
1655 
1656  if(hitsInMultiplet.size() > 16) {
1657  // Found > 16 hits in a multiplet which would be bad for UseHit. Truncate it
1658  hitsInMultiplet.resize(16);
1659  return;
1660  }
1661 
1662  // Don't make a multiplet that includes a tall narrow hit with short fat hits
1663  if(nNarrow == hitsInMultiplet.size()) return;
1664  if(nNarrow == 0) return;
1665 
1666  if(theHitIsNarrow && theHit == imTall) {
1667  // theHit is narrow and it is the highest amplitude hit in the multiplet. Ignore any
1668  // others that are short and fat
1669  auto tmp = hitsInMultiplet;
1670  tmp.resize(1);
1671  tmp[0] = theHit;
1672  hitsInMultiplet = tmp;
1673  } else {
1674  // theHit is not narrow and it is not the tallest. Ignore a single hit if it is
1675  // the tallest and narrow
1676  auto& hit = (*evt.allHits)[slc.slHits[imTall].allHitsIndex];
1677  if(hit.RMS() < narrowHitCut) {
1678  unsigned short killMe = 0;
1679  for(unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
1680  if(hitsInMultiplet[ii] == imTall) {
1681  killMe = ii;
1682  break;
1683  }
1684  } // ii
1685  hitsInMultiplet.erase(hitsInMultiplet.begin() + killMe);
1686  } // slc.slHits[imTall].RMS < narrowHitCut
1687  } // narrow / tall test
1688 
1689  } // GetHitMultiplet
1690 
1691 
1693  float HitTimeErr(TCSlice& slc, unsigned int iht)
1694  {
1695  if(iht > slc.slHits.size() - 1) return 0;
1696  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1697  return hit.RMS() * tcc.unitsPerTick * tcc.hitErrFac * hit.Multiplicity();
1698  } // HitTimeErr
1699 
1701  float HitsTimeErr2(TCSlice& slc, const std::vector<unsigned int>& hitVec)
1702  {
1703  // Estimates the error^2 of the time using all hits in hitVec
1704  if(hitVec.empty()) return 0;
1705  float err = tcc.hitErrFac * HitsRMSTime(slc, hitVec, kUnusedHits);
1706  return err * err;
1707  } // HitsTimeErr2
1708 
1709 
1711  void ChkStopEndPts(TCSlice& slc, Trajectory& tj, bool prt)
1712  {
1713  // Analyze the end of the Tj after crawling has stopped to see if any of the points
1714  // should be used
1715  // TODO: This function results in a small loss of efficiency and needs work. Perhaps by requir
1716 
1717  if(!tcc.useAlg[kChkStopEP]) return;
1718  if(tj.AlgMod[kJunkTj]) return;
1719  if(tj.Strategy[kStiffEl]) return;
1720 
1721  unsigned short endPt = tj.EndPt[1];
1722  // ignore VLA Tjs
1723  if(tj.Pts[endPt].AngleCode > 1) return;
1724  // don't get too carried away with this
1725  if(tj.Pts.size() - endPt > 10) return;
1726 
1727  // Get a list of hits a few wires beyond the last point on the Tj
1728  geo::PlaneID planeID = DecodeCTP(tj.CTP);
1729  unsigned short plane = planeID.Plane;
1730 
1731  // find the last point that has hits on it
1732  unsigned short lastPt = tj.Pts.size() - 1;
1733  for(lastPt = tj.Pts.size() - 1; lastPt >= tj.EndPt[1]; --lastPt) if(!tj.Pts[lastPt].Hits.empty()) break;
1734  auto& lastTP = tj.Pts[lastPt];
1735 
1736  if(tcc.dbgStp) {
1737  mf::LogVerbatim("TC")<<"CSEP: checking "<<tj.ID<<" endPt "<<endPt<<" Pts size "<<tj.Pts.size()<<" lastPt Pos "<<PrintPos(slc, lastTP.Pos);
1738  }
1739 
1740  // Check the charge and delta of the last point if there were many points fit
1741  // BB 11/26/ 2018 This is a bad idea for stopping tracks
1742  if(!tcc.useAlg[kNewStpCuts]) {
1743  if(lastTP.NTPsFit > 10 && lastTP.DeltaRMS > 0 && (lastTP.Delta / lastTP.DeltaRMS) > 3 && lastTP.ChgPull > 3) {
1744  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Removing last TP with large Delta "<<lastTP.Delta<<" and large ChgPull "<<lastTP.ChgPull;
1745  UnsetUsedHits(slc, lastTP);
1746  tj.AlgMod[kChkStopEP] = true;
1747  SetEndPoints(tj);
1748  // check again
1749  auto& tp = tj.Pts[tj.EndPt[1]];
1750  if(tp.DeltaRMS > 0 && (tp.Delta / tp.DeltaRMS) > 3 && tp.ChgPull > 3) {
1751  UnsetUsedHits(slc, tp);
1752  SetEndPoints(tj);
1753  }
1754  return;
1755  }
1756  }
1757 
1758  TrajPoint ltp;
1759  ltp.CTP = tj.CTP;
1760  ltp.Pos = tj.Pts[endPt].Pos;
1761  ltp.Dir = tj.Pts[endPt].Dir;
1762  double stepSize = std::abs(1/ltp.Dir[0]);
1763  std::array<int, 2> wireWindow;
1764  std::array<float, 2> timeWindow;
1765  std::vector<int> closeHits;
1766  bool isClean = true;
1767  for(unsigned short step = 0; step < 10; ++step) {
1768  for(unsigned short iwt = 0; iwt < 2; ++iwt) ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
1769  int wire = std::nearbyint(ltp.Pos[0]);
1770  wireWindow[0] = wire;
1771  wireWindow[1] = wire;
1772  timeWindow[0] = ltp.Pos[1] - 5;
1773  timeWindow[1] = ltp.Pos[1] + 5;
1774  bool hitsNear;
1775  auto tmp = FindCloseHits(slc, wireWindow, timeWindow, plane, kAllHits, true, hitsNear);
1776  // add close hits that are not associated with this tj
1777  for(auto iht : tmp) if(slc.slHits[iht].InTraj != tj.ID) closeHits.push_back(iht);
1778  float nWiresPast = 0;
1779  // Check beyond the end of the trajectory to see if there are hits there
1780  if(ltp.Dir[0] > 0) {
1781  // stepping +
1782  nWiresPast = ltp.Pos[0] - lastTP.Pos[0];
1783  } else {
1784  // stepping -
1785  nWiresPast = lastTP.Pos[0] - ltp.Pos[0];
1786  }
1787  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Found "<<tmp.size()<<" hits near pos "<<PrintPos(slc, ltp.Pos)<<" nWiresPast "<<nWiresPast;
1788  if(nWiresPast > 0.5) {
1789  if(!tmp.empty()) isClean = false;
1790  if(nWiresPast > 1.5) break;
1791  } // nWiresPast > 0.5
1792  } // step
1793 
1794  // count the number of available hits
1795  unsigned short nAvailable = 0;
1796  for(auto iht : closeHits) if(slc.slHits[iht].InTraj == 0) ++nAvailable;
1797 
1798  if(tcc.dbgStp) {
1799  mf::LogVerbatim myprt("TC");
1800  myprt<<"closeHits";
1801  for(auto iht : closeHits) myprt<<" "<<PrintHit(slc.slHits[iht]);
1802  myprt<<" nAvailable "<<nAvailable;
1803  myprt<<" isClean "<<isClean;
1804  } // prt
1805 
1806  if(!isClean || nAvailable != closeHits.size()) return;
1807 
1808  unsigned short originalEndPt = tj.EndPt[1] + 1;
1809  // looks clean so use all the hits
1810  for(unsigned short ipt = originalEndPt; ipt <= lastPt; ++ipt) {
1811  auto& tp = tj.Pts[ipt];
1812  bool hitsAdded = false;
1813  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1814  // This shouldn't happen but check anyway
1815  if(slc.slHits[tp.Hits[ii]].InTraj != 0) continue;
1816  tp.UseHit[ii] = true;
1817  slc.slHits[tp.Hits[ii]].InTraj = tj.ID;
1818  hitsAdded = true;
1819  } // ii
1820  if(hitsAdded) DefineHitPos(slc, tp);
1821  } // ipt
1822  tj.AlgMod[kChkStopEP] = true;
1823  SetEndPoints(tj);
1824  // Re-fitting the end might be a good idea but it's probably not necessary. The
1825  // values of Delta should have already been filled
1826 
1827  // require a Bragg peak
1828  ChkStop(slc, tj);
1829  if(!tj.StopFlag[1][kBragg]) {
1830  // restore the original
1831  for(unsigned short ipt = originalEndPt; ipt <= lastPt; ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
1832  SetEndPoints(tj);
1833  } // no Bragg Peak
1834 
1835  UpdateTjChgProperties("CSEP", slc, tj, prt);
1836 
1837  } // ChkStopEndPts
1838 
1841  {
1842  // defines HitPos, HitPosErr2 and Chg for the used hits in the trajectory point
1843 
1844  tp.Chg = 0;
1845  if(tp.Hits.empty()) return;
1846 
1847  unsigned short nused = 0;
1848  unsigned int iht = 0;
1849  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1850  if(!tp.UseHit[ii]) continue;
1851  ++nused;
1852  iht = tp.Hits[ii];
1853  }
1854  if(nused == 0) return;
1855 
1856  // don't bother with rest of this if there is only one hit since it can
1857  // only reside on one wire
1858  if(nused == 1) {
1859  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1860  tp.Chg = hit.Integral();
1861  tp.HitPos[0] = hit.WireID().Wire;
1862  tp.HitPos[1] = hit.PeakTime() * tcc.unitsPerTick;
1864  // give it a huge error^2 since the position is not well defined
1865  tp.HitPosErr2 = 100;
1866  } else {
1867  // Normalize to 1 WSE path length
1868  float pathInv = std::abs(tp.Dir[0]);
1869  if(pathInv < 0.05) pathInv = 0.05;
1870  tp.Chg *= pathInv;
1871  float wireErr = tp.Dir[1] * 0.289;
1872  float timeErr = tp.Dir[0] * HitTimeErr(slc, iht);
1873  tp.HitPosErr2 = wireErr * wireErr + timeErr * timeErr;
1874  }
1875  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"DefineHitPos: singlet "<<std::fixed<<std::setprecision(1)<<tp.HitPos[0]<<":"<<(int)(tp.HitPos[1]/tcc.unitsPerTick)<<" ticks. HitPosErr "<<sqrt(tp.HitPosErr2);
1876  return;
1877  } // nused == 1
1878 
1879  // multiple hits possibly on different wires
1880  std::vector<unsigned int> hitVec;
1881  tp.Chg = 0;
1882  std::array<float, 2> newpos;
1883  float chg;
1884  newpos[0] = 0;
1885  newpos[1] = 0;
1886  // Find the wire range for hits used in the TP
1887  unsigned int loWire = INT_MAX;
1888  unsigned int hiWire = 0;
1889  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1890  if(!tp.UseHit[ii]) continue;
1891  unsigned int iht = tp.Hits[ii];
1892  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1893  chg = hit.Integral();
1894  unsigned int wire = hit.WireID().Wire;
1895  if(wire < loWire) loWire = wire;
1896  if(wire > hiWire) hiWire = wire;
1897  newpos[0] += chg * wire;
1898  newpos[1] += chg * hit.PeakTime();
1899  tp.Chg += chg;
1900  hitVec.push_back(iht);
1901  } // ii
1902 
1903  if(tp.Chg == 0) return;
1904 
1905  tp.HitPos[0] = newpos[0] / tp.Chg;
1906  tp.HitPos[1] = newpos[1] * tcc.unitsPerTick / tp.Chg;
1907  // Normalize to 1 WSE path length
1908  float pathInv = std::abs(tp.Dir[0]);
1909  if(pathInv < 0.05) pathInv = 0.05;
1910  tp.Chg *= pathInv;
1911  // Error is the wire error (1/sqrt(12))^2 if all hits are on one wire.
1912  // Scale it by the wire range
1913  float dWire = 1 + hiWire - loWire;
1914  float wireErr = tp.Dir[1] * dWire * 0.289;
1915  float timeErr2 = tp.Dir[0] * tp.Dir[0] * HitsTimeErr2(slc, hitVec);
1916  tp.HitPosErr2 = wireErr * wireErr + timeErr2;
1917  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"DefineHitPos: multiplet "<<std::fixed<<std::setprecision(1)<<tp.HitPos[0]<<":"<<(int)(tp.HitPos[1]/tcc.unitsPerTick)<<" ticks. HitPosErr "<<sqrt(tp.HitPosErr2);
1918 
1919  } // DefineHitPos
1920 
1921 
1923  void FindUseHits(TCSlice& slc, Trajectory& tj, unsigned short ipt, float maxDelta, bool useChg)
1924  {
1925  // Hits have been associated with trajectory point ipt but none are used. Here we will
1926  // decide which hits to use.
1927 
1928  if(ipt > tj.Pts.size() - 1) return;
1929  TrajPoint& tp = tj.Pts[ipt];
1930 
1931  if(tp.Hits.empty()) return;
1932 /* Nov 6, 2018. This is a bad idea if widely separated hits were associated with the TP
1933  // Use all available hits on the last pass for the first few points when starting out
1934  if(ipt < 3 && tj.Pass == tcc.minPts.size() - 1) {
1935  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1936  unsigned int iht = tp.Hits[ii];
1937  if(slc.slHits[iht].InTraj > 0) continue;
1938  tp.UseHit[ii] = true;
1939  slc.slHits[iht].InTraj = tj.ID;
1940  }
1941  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"FUH: Using all hits on seed trajectory on the last pass";
1942  return;
1943  } // last pass for the first two points
1944 */
1945  // don't check charge when starting out
1946  if(ipt < 5) useChg = false;
1947  float chgPullCut = 1000;
1948  if(useChg) chgPullCut = tcc.chargeCuts[0];
1949  // open it up for RevProp, since we might be following a stopping track
1950  if(tj.AlgMod[kRvPrp]) chgPullCut *= 2;
1951  if(tj.MCSMom < 30) chgPullCut *= 2;
1952 
1953  bool ignoreLongPulseHits = false;
1954  unsigned short npts = tj.EndPt[1] - tj.EndPt[0] + 1;
1955  if(tcc.useAlg[kNewStpCuts] && (npts < 10 || tj.AlgMod[kRvPrp])) ignoreLongPulseHits = true;
1956 
1957  float expectedHitsRMS = ExpectedHitsRMS(slc, tp);
1958  if(tcc.dbgStp) {
1959  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(slc, tp, kUnusedHits)<<" ExpectedHitsRMS "<<(int)expectedHitsRMS<<" AngCode "<<tp.AngleCode;
1960  }
1961 
1962  // inverse of the path length for normalizing hit charge to 1 WSE unit
1963  float pathInv = std::abs(tp.Dir[0]);
1964  if(pathInv < 0.05) pathInv = 0.05;
1965 
1966  // Find the hit that has the smallest delta and the number of available hits
1967  tp.Delta = maxDelta;
1968  float delta;
1969  unsigned short imbest = USHRT_MAX;
1970  std::vector<float> deltas(tp.Hits.size(), 100);
1971  // keep track of the best delta - even if it is used
1972  float bestDelta = maxDelta;
1973  unsigned short nAvailable = 0;
1974  unsigned short firstAvailable = USHRT_MAX;
1975  unsigned short lastAvailable = USHRT_MAX;
1976  unsigned short firstUsed = USHRT_MAX;
1977  unsigned short imBadRecoHit = USHRT_MAX;
1978  bool bestHitLongPulse = false;
1979  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1980  tp.UseHit[ii] = false;
1981  unsigned int iht = tp.Hits[ii];
1982  delta = PointTrajDOCA(slc, iht, tp);
1983  if(delta < bestDelta) bestDelta = delta;
1984  if(slc.slHits[iht].InTraj > 0) {
1985  if(firstUsed == USHRT_MAX) firstUsed = ii;
1986  continue;
1987  }
1988  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1989  if(ignoreLongPulseHits && LongPulseHit(hit)) continue;
1990  if(hit.GoodnessOfFit() < 0 || hit.GoodnessOfFit() > 100) imBadRecoHit = ii;
1991  if(firstAvailable == USHRT_MAX) firstAvailable = ii;
1992  lastAvailable = ii;
1993  ++nAvailable;
1994  if(tcc.dbgStp) {
1995  if(useChg) {
1996  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" "<<ii<<" "<<PrintHit(slc.slHits[iht])<<" delta "<<delta<<" Norm Chg "<<(int)(hit.Integral() * pathInv);
1997  } else {
1998  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" "<<ii<<" "<<PrintHit(slc.slHits[iht])<<" delta "<<delta;
1999  }
2000  } // tcc.dbgStp
2001  deltas[ii] = delta;
2002  if(delta < tp.Delta) {
2003  tp.Delta = delta;
2004  imbest = ii;
2005  bestHitLongPulse = LongPulseHit(hit);
2006  }
2007  } // ii
2008 
2009  float chgWght = 0.5;
2010 
2011  if(tcc.dbgStp) 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;
2012  if(imbest == USHRT_MAX || nAvailable == 0) return;
2013  unsigned int bestDeltaHit = tp.Hits[imbest];
2014 
2015  // just use the best hit if we are tracking a high energy electron and the best hit is a long pulse hit
2016  if(tj.Strategy[kStiffEl] && bestHitLongPulse) {
2017  tp.UseHit[imbest] = true;
2018  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2019  return;
2020  }
2021 
2022  // Don't try to use a multiplet if a hit in the middle is in a different trajectory
2023  if(tp.Hits.size() > 2 && nAvailable > 1 && firstUsed != USHRT_MAX && firstAvailable < firstUsed && lastAvailable > firstUsed) {
2024  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" A hit in the middle of the multiplet is used. Use only the best hit";
2025  tp.UseHit[imbest] = true;
2026  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2027  return;
2028  } // Used hit inside multiplet
2029 
2030  if(tp.AngleCode == 1) {
2031  // Get the hits that are in the same multiplet as bestDeltaHit
2032  std::vector<unsigned int> hitsInMultiplet;
2033  unsigned short localIndex;
2034  GetHitMultiplet(slc, bestDeltaHit, hitsInMultiplet, localIndex);
2035  if(tcc.dbgStp) {
2036  mf::LogVerbatim myprt("TC");
2037  myprt<<" bestDeltaHit "<<PrintHit(slc.slHits[bestDeltaHit]);
2038  myprt<<" in multiplet:";
2039  for(auto& iht : hitsInMultiplet) myprt<<" "<<PrintHit(slc.slHits[iht]);
2040  }
2041  // Consider the case where a bad reco hit might be better. It is probably wider and
2042  // has more charge
2043  if(imBadRecoHit != USHRT_MAX) {
2044  unsigned int iht = tp.Hits[imBadRecoHit];
2045  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2046  if(hit.RMS() > HitsRMSTick(slc, hitsInMultiplet, kUnusedHits)) {
2047  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Using imBadRecoHit "<<PrintHit(slc.slHits[iht]);
2048  tp.UseHit[imBadRecoHit] = true;
2049  slc.slHits[iht].InTraj = tj.ID;
2050  return;
2051  }
2052  } // bad reco hit
2053  // Use the hits in the multiplet instead
2054  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2055  unsigned int iht = tp.Hits[ii];
2056  if(slc.slHits[iht].InTraj > 0) continue;
2057  if(std::find(hitsInMultiplet.begin(), hitsInMultiplet.end(), iht) == hitsInMultiplet.end()) continue;
2058  tp.UseHit[ii] = true;
2059  slc.slHits[iht].InTraj = tj.ID;
2060  } // ii
2061  return;
2062  } // isLA
2063 
2064  // don't use the best UNUSED hit if the best delta is for a USED hit and it is much better
2065  // TY: ignore for RevProp
2066  if(bestDelta < 0.5 * tp.Delta && !tj.AlgMod[kRvPrp]) return;
2067 
2068  if(!useChg || (useChg && (tj.AveChg <= 0 || tj.ChgRMS <= 0))) {
2069  // necessary quantities aren't available for more careful checking
2070  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" tj.AveChg "<<tj.AveChg<<" or tj.ChgRMS "<<tj.ChgRMS<<". Use the best hit";
2071  tp.UseHit[imbest] = true;
2072  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2073  return;
2074  }
2075 
2076  // Don't try to get fancy if we are tracking a stiff tj
2077  if(tj.PDGCode == 13 && bestDelta < 0.5) {
2078  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Tracking muon. Use the best hit";
2079  tp.UseHit[imbest] = true;
2080  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2081  return;
2082  }
2083 
2084  // The best hit is the only one available or this is a small angle trajectory
2085  if(nAvailable == 1 || tp.AngleCode == 0) {
2086  auto& hit = (*evt.allHits)[slc.slHits[bestDeltaHit].allHitsIndex];
2087  float aveChg = tp.AveChg;
2088  if(aveChg <= 0) aveChg = tj.AveChg;
2089  if(aveChg <= 0) aveChg = hit.Integral();
2090  float chgRMS = tj.ChgRMS;
2091  if(chgRMS < 0.2) chgRMS = 0.2;
2092  float bestDeltaHitChgPull = std::abs(hit.Integral() * pathInv / aveChg - 1) / chgRMS;
2093  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" bestDeltaHitChgPull "<<bestDeltaHitChgPull<<" chgPullCut "<<chgPullCut;
2094  if(bestDeltaHitChgPull < chgPullCut || tp.Delta < 0.1) {
2095  tp.UseHit[imbest] = true;
2096  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2097  } // good charge or very good delta
2098  return;
2099  } // bestDeltaHitMultiplicity == 1
2100 
2101  // Find the expected width for the angle of this TP (ticks)
2102  float expectedWidth = ExpectedHitsRMS(slc, tp);
2103 
2104  // Handle two available hits
2105  if(nAvailable == 2) {
2106  // See if these two are in the same multiplet and both are available
2107  std::vector<unsigned int> tHits;
2108  unsigned short localIndex;
2109  GetHitMultiplet(slc, bestDeltaHit, tHits, localIndex);
2110  // ombest is the index of the other hit in tp.Hits that is in the same multiplet as bestDeltaHit
2111  // if we find it
2112  unsigned short ombest = USHRT_MAX;
2113  unsigned int otherHit = INT_MAX;
2114  if(tHits.size() == 2) {
2115  otherHit = tHits[1 - localIndex];
2116  // get the index of this hit in tp.Hits
2117  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2118  if(slc.slHits[tp.Hits[ii]].InTraj > 0) continue;
2119  if(tp.Hits[ii] == otherHit) {
2120  ombest = ii;
2121  break;
2122  }
2123  } // ii
2124  } // tHits.size() == 2
2125  if(tcc.dbgStp) {
2126  mf::LogVerbatim("TC")<<" Doublet: imbest "<<imbest<<" ombest "<<ombest;
2127  }
2128  // The other hit exists in the tp and it is available
2129  if(ombest < tp.Hits.size()) {
2130  // compare the best delta hit and the other hit separately and the doublet as a merged pair
2131  float bestHitDeltaErr = std::abs(tp.Dir[1]) * 0.17 + std::abs(tp.Dir[0]) * HitTimeErr(slc, bestDeltaHit);
2132  // Construct a FOM starting with the delta pull
2133  float bestDeltaHitFOM = deltas[imbest] / bestHitDeltaErr;
2134  if(bestDeltaHitFOM < 0.5) bestDeltaHitFOM = 0.5;
2135  // multiply by the charge pull if it is significant
2136  auto& hit = (*evt.allHits)[slc.slHits[bestDeltaHit].allHitsIndex];
2137  float bestDeltaHitChgPull = std::abs(hit.Integral() * pathInv / tp.AveChg - 1) / tj.ChgRMS;
2138  if(bestDeltaHitChgPull > 1) bestDeltaHitFOM *= chgWght * bestDeltaHitChgPull;
2139  // scale by the ratio
2140  float rmsRat = hit.RMS() / expectedWidth;
2141  if(rmsRat < 1) rmsRat = 1 / rmsRat;
2142  bestDeltaHitFOM *= rmsRat;
2143  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" bestDeltaHit FOM "<<deltas[imbest]/bestHitDeltaErr<<" bestDeltaHitChgPull "<<bestDeltaHitChgPull<<" rmsRat "<<rmsRat<<" bestDeltaHitFOM "<<bestDeltaHitFOM;
2144  // Now do the same for the other hit
2145  float otherHitDeltaErr = std::abs(tp.Dir[1]) * 0.17 + std::abs(tp.Dir[0]) * HitTimeErr(slc, otherHit);
2146  float otherHitFOM = deltas[ombest] / otherHitDeltaErr;
2147  if(otherHitFOM < 0.5) otherHitFOM = 0.5;
2148  auto& ohit = (*evt.allHits)[slc.slHits[otherHit].allHitsIndex];
2149  float otherHitChgPull = std::abs(ohit.Integral() * pathInv / tp.AveChg - 1) / tj.ChgRMS;
2150  if(otherHitChgPull > 1) otherHitFOM *= chgWght * otherHitChgPull;
2151  rmsRat = ohit.RMS() / expectedWidth;
2152  if(rmsRat < 1) rmsRat = 1 / rmsRat;
2153  otherHitFOM *= rmsRat;
2154  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" otherHit FOM "<<deltas[ombest]/otherHitDeltaErr<<" otherHitChgPull "<<otherHitChgPull<<" rmsRat "<<rmsRat<<" otherHitFOM "<<otherHitFOM;
2155  // And for the doublet
2156  float doubletChg = hit.Integral() + ohit.Integral();
2157  float doubletTime = (hit.Integral() * hit.PeakTime() + ohit.Integral() * ohit.PeakTime()) / doubletChg;
2158  doubletChg *= pathInv;
2159  doubletTime *= tcc.unitsPerTick;
2160  float doubletWidthTick = TPHitsRMSTick(slc, tp, kUnusedHits);
2161  float doubletRMSTimeErr = doubletWidthTick * tcc.unitsPerTick;
2162  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" doublet Chg "<<doubletChg<<" doubletTime "<<doubletTime<<" doubletRMSTimeErr "<<doubletRMSTimeErr;
2163  float doubletFOM = PointTrajDOCA(slc, tp.Pos[0], doubletTime, tp) / doubletRMSTimeErr;
2164  if(doubletFOM < 0.5) doubletFOM = 0.5;
2165  float doubletChgPull = std::abs(doubletChg * pathInv / tp.AveChg - 1) / tj.ChgRMS;
2166  if(doubletChgPull > 1) doubletFOM *= chgWght * doubletChgPull;
2167  rmsRat = doubletWidthTick / expectedWidth;
2168  if(rmsRat < 1) rmsRat = 1 / rmsRat;
2169  doubletFOM *= rmsRat;
2170  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" doublet FOM "<<PointTrajDOCA(slc, tp.Pos[0], doubletTime, tp)/doubletRMSTimeErr<<" doubletChgPull "<<doubletChgPull<<" rmsRat "<<rmsRat<<" doubletFOM "<<doubletFOM;
2171  if(doubletFOM < bestDeltaHitFOM && doubletFOM < otherHitFOM) {
2172  tp.UseHit[imbest] = true;
2173  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2174  tp.UseHit[ombest] = true;
2175  slc.slHits[otherHit].InTraj = tj.ID;
2176  } else {
2177  // the doublet is not the best
2178  if(bestDeltaHitFOM < otherHitFOM) {
2179  tp.UseHit[imbest] = true;
2180  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2181  } else {
2182  tp.UseHit[ombest] = true;
2183  slc.slHits[otherHit].InTraj = tj.ID;
2184  } // otherHit is the best
2185  } // doublet is not the best
2186  } else {
2187  // the other hit isn't available. Just use the singlet
2188  tp.UseHit[imbest] = true;
2189  slc.slHits[bestDeltaHit].InTraj = tj.ID;
2190  }
2191  return;
2192  } // nAvailable == 2
2193  float hitsWidth = TPHitsRMSTick(slc, tp, kUnusedHits);
2194  float maxTick = tp.Pos[1] / tcc.unitsPerTick + 0.6 * expectedWidth;
2195  float minTick = tp.Pos[1] / tcc.unitsPerTick - 0.6 * expectedWidth;
2196  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Multiplet: hitsWidth "<<hitsWidth<<" expectedWidth "<<expectedWidth<<" tick range "<<(int)minTick<<" "<<(int)maxTick;
2197  // use all of the hits in the tick window
2198  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2199  unsigned int iht = tp.Hits[ii];
2200  if(slc.slHits[iht].InTraj > 0) continue;
2201  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2202  if(hit.PeakTime() < minTick) continue;
2203  if(hit.PeakTime() > maxTick) continue;
2204  tp.UseHit[ii] = true;
2205  slc.slHits[iht].InTraj = tj.ID;
2206  }
2207 
2208  } // FindUseHits
2209 
2212  {
2213  // Looks for a soft kink in the trajectory and truncates it if one is found.
2214  // This is best done after FixTrajBegin has been called.
2215 
2216  if(!tcc.useAlg[kSoftKink]) return;
2217  if(tcc.useAlg[kNewStpCuts] && tj.Strategy[kStiffEl]) return;
2218  if(tj.Pts.size() < 15) return;
2219  if(tj.MCSMom < 100) return;
2220 
2221  float dang = DeltaAngle(tj.Pts[tj.EndPt[0]].Ang, tj.Pts[tj.EndPt[1]].Ang);
2222 
2223  if(tcc.dbgStp) {
2224  mf::LogVerbatim("TC")<<"FindSoftKink: "<<tj.ID<<" dang "<<dang<<" cut "<<0.5 * tcc.kinkCuts[0];
2225  }
2226  if(dang < 0.5 * tcc.kinkCuts[0]) return;
2227  // require at least 5 points fitted at the end of the trajectory
2228  unsigned short endPt = tj.EndPt[1];
2229  if(tj.Pts[endPt].NTPsFit < 5) return;
2230  if(tj.Pts[endPt].NTPsFit > endPt) return;
2231  // Estimate where where the kink would be
2232  unsigned short kinkPt = endPt - tj.Pts[endPt].NTPsFit;
2233  // Require at least 5 points in the trajectory before the kink
2234  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" kinkPt "<<kinkPt<<" NTPsFit at kinkPt "<<tj.Pts[kinkPt].NTPsFit<<" max "<<0.5 * kinkPt;
2235  if(kinkPt < 5) return;
2236  // require fewer points fitted in this region compared the number of points prior to it
2237  if(tj.Pts[kinkPt].NTPsFit > 0.5 * kinkPt) return;
2238  // scan back until we find the maximum number of points fitted
2239  unsigned short maxPtsFit = tj.Pts[kinkPt].NTPsFit;
2240  unsigned short atPt = kinkPt;
2241  for(unsigned short ipt = kinkPt; kinkPt > tj.EndPt[0] + 5; --ipt) {
2242  if(tj.Pts[ipt].NTPsFit > maxPtsFit) {
2243  maxPtsFit = tj.Pts[ipt].NTPsFit;
2244  atPt = ipt;
2245  }
2246  // stop scanning when the max starts falling
2247  if(tj.Pts[ipt].NTPsFit < maxPtsFit) break;
2248  if(ipt == 0) break;
2249  } // ipt
2250  if(atPt < 5) return;
2251  // require the trajectory be straight before the kink - the section we are going to keep
2252  if(MCSMom(slc, tj, tj.EndPt[0], atPt) < 500) return;
2253  // release the hits in TPs after this point
2254  for(unsigned short ipt = atPt; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
2255  // Truncate the trajectory at this point
2256  tj.Pts.resize(atPt + 1);
2257  SetEndPoints(tj);
2258  tj.AlgMod[kSoftKink] = true;
2259  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" truncated trajectory at "<<PrintPos(slc, tj.Pts[tj.Pts.size()-1]);
2260 
2261  } // FindSoftKinks
2262 
2264  void FillGaps(TCSlice& slc, Trajectory& tj)
2265  {
2266  // Fill in any gaps in the trajectory with close hits regardless of charge (well maybe not quite that)
2267 
2268  if(!tcc.useAlg[kFillGap]) return;
2269  if(tj.AlgMod[kJunkTj]) return;
2270 
2271  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"FG: Check Tj "<<tj.ID<<" from "<<PrintPos(slc, tj.Pts[tj.EndPt[0]])<<" to "<<PrintPos(slc, tj.Pts[tj.EndPt[1]]);
2272 
2273  // start with the first point that has charge
2274  short firstPtWithChg = tj.EndPt[0];
2275  bool first = true;
2276  float maxDelta = 1;
2277  // don't let MCSMom suffer too much while filling gaps
2278  short minMCSMom = 0.7 * tj.MCSMom;
2279  while(firstPtWithChg < tj.EndPt[1]) {
2280  short nextPtWithChg = firstPtWithChg + 1;
2281  // find the next point with charge
2282  for(nextPtWithChg = firstPtWithChg + 1; nextPtWithChg < tj.EndPt[1]; ++nextPtWithChg) {
2283  if(tj.Pts[nextPtWithChg].Chg > 0) break;
2284  } // nextPtWithChg
2285  if(nextPtWithChg == firstPtWithChg + 1) {
2286  // the next point has charge
2287  ++firstPtWithChg;
2288  continue;
2289  }
2290  // Found a gap. Require at least two consecutive points with charge after the gap
2291  if(nextPtWithChg < (tj.EndPt[1] - 1) && tj.Pts[nextPtWithChg + 1].Chg == 0) {
2292  firstPtWithChg = nextPtWithChg;
2293  continue;
2294  }
2295  // 10/1/2018 BB This shouldn't be a requirement
2296  // Compare the charge before and after
2297  if(!tcc.useAlg[kNewStpCuts] && tj.Pts[firstPtWithChg].Chg > 0) {
2298  float chgrat = tj.Pts[nextPtWithChg].Chg / tj.Pts[firstPtWithChg].Chg;
2299  if(chgrat < 0.7 || chgrat > 1.4) {
2300  firstPtWithChg = nextPtWithChg;
2301  continue;
2302  }
2303  }
2304 
2305  // Make a bare trajectory point at firstPtWithChg that points to nextPtWithChg
2306  TrajPoint tp;
2307  if(!MakeBareTrajPoint(slc, tj.Pts[firstPtWithChg], tj.Pts[nextPtWithChg], tp)) {
2308  tj.IsGood = false;
2309  return;
2310  }
2311  // Find the maximum delta between hits and the trajectory Pos for all
2312  // hits on this trajectory
2313  if(first) {
2314  maxDelta = 2.5 * MaxHitDelta(slc, tj);
2315  first = false;
2316  } // first
2317  // define a loose charge cut using the average charge at the first point with charge
2318  float maxChg = tj.Pts[firstPtWithChg].AveChg * (1 + 2 * tcc.chargeCuts[0] * tj.ChgRMS);
2319  // Eliminate the charge cut altogether if we are close to an end
2320  if(tj.Pts.size() < 10) {
2321  maxChg = 1E6;
2322  } else {
2323  short chgCutPt = tj.EndPt[0] + 5;
2324  if(firstPtWithChg < chgCutPt) {
2325  // gap is near end 0
2326  maxChg = 1E6;
2327  } else {
2328  // check for gap near end 1
2329  chgCutPt = tj.EndPt[1] - 5;
2330  if(chgCutPt < tj.EndPt[0]) chgCutPt = tj.EndPt[0];
2331  if(nextPtWithChg > chgCutPt) maxChg = 1E6;
2332  }
2333  }
2334 
2335  // fill in the gap
2336  for(unsigned short mpt = firstPtWithChg + 1; mpt < nextPtWithChg; ++mpt) {
2337  if(tj.Pts[mpt].Chg > 0) {
2338  mf::LogWarning("TC")<<"FillGaps coding error: firstPtWithChg "<<firstPtWithChg<<" mpt "<<mpt<<" nextPtWithChg "<<nextPtWithChg;
2339  slc.isValid = false;
2340  return;
2341  }
2342  bool filled = false;
2343  float chg = 0;
2344  for(unsigned short ii = 0; ii < tj.Pts[mpt].Hits.size(); ++ii) {
2345  unsigned int iht = tj.Pts[mpt].Hits[ii];
2346  if(slc.slHits[iht].InTraj > 0) continue;
2347  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2348  float delta = PointTrajDOCA(slc, iht, tp);
2349  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" FG: "<<PrintPos(slc,tj.Pts[mpt])<<" hit "<<PrintHit(slc.slHits[iht])<<" delta "<<delta<<" maxDelta "<<maxDelta<<" Chg "<<hit.Integral()<<" maxChg "<<maxChg;
2350  if(delta > maxDelta) continue;
2351  tj.Pts[mpt].UseHit[ii] = true;
2352  slc.slHits[iht].InTraj = tj.ID;
2353  chg += hit.Integral();
2354  filled = true;
2355  } // ii
2356  if(chg > maxChg || MCSMom(slc, tj) < minMCSMom) {
2357  // don't use these hits after all
2358  UnsetUsedHits(slc, tj.Pts[mpt]);
2359  filled = false;
2360  }
2361  if(filled) {
2362  DefineHitPos(slc, tj.Pts[mpt]);
2363  tj.AlgMod[kFillGap] = true;
2364  if(tcc.dbgStp) {
2365  PrintTrajPoint("FG", slc, mpt, tj.StepDir, tj.Pass, tj.Pts[mpt]);
2366  mf::LogVerbatim("TC")<<"Check MCSMom "<<MCSMom(slc, tj);
2367  }
2368  } // filled
2369  } // mpt
2370  firstPtWithChg = nextPtWithChg;
2371  } // firstPtWithChg
2372 
2373  if(tj.AlgMod[kFillGap]) tj.MCSMom = MCSMom(slc, tj);
2374 
2375  } // FillGaps
2376 
2379  {
2380  // Check for many unused hits in high multiplicity TPs in work and try to use them
2381 
2382  if(!tcc.useAlg[kCHMUH]) return;
2383 
2384  // This code might do bad things to short trajectories
2385  if(NumPtsWithCharge(slc, tj, true) < 6) return;
2386  if(tj.EndPt[0] == tj.EndPt[1]) return;
2387  // Angle code 0 tjs shouldn't have any high multiplicity hits added to them
2388  if(tj.Pts[tj.EndPt[1]].AngleCode == 0) return;
2389 
2390  // count the number of unused hits multiplicity > 1 hits and decide
2391  // if the unused hits should be used. This may trigger another
2392  // call to StepAway
2393  unsigned short ii, stopPt;
2394  // Use this to see if the high multiplicity Pts are mostly near
2395  // the end or further upstream
2396  unsigned short lastMult1Pt = USHRT_MAX;
2397  // the number of TPs with > 1 hit (HiMult)
2398  unsigned short nHiMultPt = 0;
2399  // the total number of hits associated with HiMult TPs
2400  unsigned short nHiMultPtHits = 0;
2401  // the total number of used hits associated with HiMult TPs
2402  unsigned short nHiMultPtUsedHits = 0;
2403  unsigned int iht;
2404  // start counting at the leading edge and break if a hit
2405  // is found that is used in a trajectory
2406  bool doBreak = false;
2407  unsigned short jj;
2408  for(ii = 1; ii < tj.Pts.size(); ++ii) {
2409  stopPt = tj.EndPt[1] - ii;
2410  for(jj = 0; jj < tj.Pts[stopPt].Hits.size(); ++jj) {
2411  iht = tj.Pts[stopPt].Hits[jj];
2412  if(slc.slHits[iht].InTraj > 0) {
2413  doBreak = true;
2414  break;
2415  }
2416  } // jj
2417  if(doBreak) break;
2418  // require 2 consecutive multiplicity = 1 points
2419  if(lastMult1Pt == USHRT_MAX && tj.Pts[stopPt].Hits.size() == 1 && tj.Pts[stopPt-1].Hits.size() == 1) lastMult1Pt = stopPt;
2420  if(tj.Pts[stopPt].Hits.size() > 1) {
2421  ++nHiMultPt;
2422  nHiMultPtHits += tj.Pts[stopPt].Hits.size();
2423  nHiMultPtUsedHits += NumHitsInTP(tj.Pts[stopPt], kUsedHits);
2424  } // high multiplicity TP
2425  // stop looking when two consecutive single multiplicity TPs are found
2426  if(lastMult1Pt != USHRT_MAX) break;
2427  if(stopPt == 1) break;
2428  } // ii
2429  // Don't do this if there aren't a lot of high multiplicity TPs
2430  float fracHiMult = (float)nHiMultPt / (float)ii;
2431  if(lastMult1Pt != USHRT_MAX) {
2432  float nchk = tj.EndPt[1] - lastMult1Pt + 1;
2433  fracHiMult = (float)nHiMultPt / nchk;
2434  } else {
2435  fracHiMult = (float)nHiMultPt / (float)ii;
2436  }
2437  float fracHitsUsed = 0;
2438  if(nHiMultPt > 0 && nHiMultPtHits > 0) fracHitsUsed = (float)nHiMultPtUsedHits / (float)nHiMultPtHits;
2439  // Use this to limit the number of points fit for trajectories that
2440  // are close the LA tracking cut
2441  ii = tj.EndPt[1];
2442  bool sortaLargeAngle = (AngleRange(tj.Pts[ii]) == 1);
2443 
2444  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"CHMUH: First InTraj stopPt "<<stopPt<<" fracHiMult "<<fracHiMult<<" fracHitsUsed "<<fracHitsUsed<<" lastMult1Pt "<<lastMult1Pt<<" sortaLargeAngle "<<sortaLargeAngle;
2445  if(fracHiMult < 0.3) return;
2446  if(fracHitsUsed > 0.98) return;
2447 
2448  float maxDelta = 2.5 * MaxHitDelta(slc, tj);
2449 
2450  if(tcc.dbgStp) {
2451  mf::LogVerbatim("TC")<<" Pts size "<<tj.Pts.size()<<" nHiMultPt "<<nHiMultPt<<" nHiMultPtHits "<<nHiMultPtHits<<" nHiMultPtUsedHits "<<nHiMultPtUsedHits<<" sortaLargeAngle "<<sortaLargeAngle<<" maxHitDelta "<<maxDelta;
2452  }
2453 
2454  // Use next pass cuts if available
2455  if(sortaLargeAngle && tj.Pass < tcc.minPtsFit.size()-1) ++tj.Pass;
2456 
2457  // Make a copy of tj in case something bad happens
2458  Trajectory TjCopy = tj;
2459  // and the list of used hits
2460  auto inTrajHits = PutTrajHitsInVector(tj, kUsedHits);
2461  unsigned short ipt;
2462 
2463  // unset the used hits from stopPt + 1 to the end
2464  for(ipt = stopPt + 1; ipt < tj.Pts.size(); ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
2465  SetEndPoints(tj);
2466  unsigned short killPts;
2467  float delta;
2468  bool added;
2469  for(ipt = stopPt + 1; ipt < tj.Pts.size(); ++ipt) {
2470  // add hits that are within maxDelta and re-fit at each point
2471  added = false;
2472  for(ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2473  iht = tj.Pts[ipt].Hits[ii];
2474  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" ipt "<<ipt<<" hit "<<PrintHit(slc.slHits[iht])<<" inTraj "<<slc.slHits[iht].InTraj<<" delta "<<PointTrajDOCA(slc, iht, tj.Pts[ipt]);
2475  if(slc.slHits[iht].InTraj != 0) continue;
2476  delta = PointTrajDOCA(slc, iht, tj.Pts[ipt]);
2477  if(delta > maxDelta) continue;
2478  if (!NumHitsInTP(TjCopy.Pts[ipt], kUsedHits)||TjCopy.Pts[ipt].UseHit[ii]){
2479  tj.Pts[ipt].UseHit[ii] = true;
2480  slc.slHits[iht].InTraj = tj.ID;
2481  added = true;
2482  }
2483  } // ii
2484  if(added) DefineHitPos(slc, tj.Pts[ipt]);
2485  if(tj.Pts[ipt].Chg == 0) continue;
2486  tj.EndPt[1] = ipt;
2487  // This will be incremented by one in UpdateTraj
2488  if(sortaLargeAngle) tj.Pts[ipt].NTPsFit = 2;
2489  UpdateTraj(slc, tj);
2490  if(tj.NeedsUpdate) {
2491  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"UpdateTraj failed on point "<<ipt;
2492  // Clobber the used hits from the corrupted points in tj
2493  for(unsigned short jpt = stopPt + 1; jpt <= ipt; ++jpt) {
2494  for(unsigned short jj = 0; jj < tj.Pts[jpt].Hits.size(); ++jj) {
2495  if(tj.Pts[jpt].UseHit[jj]) slc.slHits[tj.Pts[jpt].Hits[jj]].InTraj = 0;
2496  } // jj
2497  } // jpt
2498  // restore the original trajectory
2499  tj = TjCopy;
2500  // restore the hits
2501  for(unsigned short jpt = stopPt + 1; jpt <= ipt; ++jpt) {
2502  for(unsigned short jj = 0; jj < tj.Pts[jpt].Hits.size(); ++jj) {
2503  if(tj.Pts[jpt].UseHit[jj]) slc.slHits[tj.Pts[jpt].Hits[jj]].InTraj = tj.ID;
2504  } // jj
2505  } // jpt
2506  return;
2507  }
2508  GottaKink(slc, tj, killPts);
2509  if(killPts > 0) {
2510  MaskTrajEndPoints(slc, tj, killPts);
2511  if(!tj.IsGood) return;
2512  break;
2513  }
2514  if(tcc.dbgStp) PrintTrajectory("CHMUH", slc, tj, ipt);
2515  } // ipt
2516  // if we made it here it must be OK
2517  SetEndPoints(tj);
2518  // Try to extend it, unless there was a kink
2519  if(tj.StopFlag[1][kAtKink]) return;
2520  // trim the end points although this shouldn't happen
2521  if(tj.EndPt[1] != tj.Pts.size() - 1) tj.Pts.resize(tj.EndPt[1] + 1);
2522  tj.AlgMod[kCHMUH] = true;
2523  } // CheckHiMultUnusedHits
2524 
2527  {
2528  // mask off high multiplicity TPs at the end
2529  if(!tcc.useAlg[kCHMEH]) return;
2530  if(tj.StopFlag[1][kBragg]) return;
2531  if(tj.Pts.size() < 10) return;
2532  if(tj.Pts[tj.EndPt[1]].AngleCode == 0) return;
2533  // find the average multiplicity in the first half
2534  unsigned short aveMult= 0;
2535  unsigned short ipt, nhalf = tj.Pts.size() / 2;
2536  unsigned short cnt = 0;
2537  for(auto& tp : tj.Pts) {
2538  if(tp.Chg == 0) continue;
2539  aveMult += tp.Hits.size();
2540  ++cnt;
2541  if(cnt == nhalf) break;
2542  } // pt
2543  if(cnt == 0) return;
2544  aveMult /= cnt;
2545  if(aveMult == 0) aveMult = 1;
2546  // convert this into a cut
2547  aveMult *= 3;
2548  cnt = 0;
2549  for(ipt = tj.EndPt[1]; ipt > tj.EndPt[0]; --ipt) {
2550  if(tj.Pts[ipt].Chg == 0) continue;
2551  if(tj.Pts[ipt].Hits.size() > aveMult) {
2552  UnsetUsedHits(slc, tj.Pts[ipt]);
2553  ++cnt;
2554  continue;
2555  }
2556  break;
2557  } // ipt
2558  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"CHMEH multiplicity cut "<<aveMult<<" number of TPs masked off "<<cnt;
2559  if(cnt > 0) {
2560  tj.AlgMod[kCHMEH] = true;
2561  SetEndPoints(tj);
2562  }
2563  } // CheckHiMultEndHits
2564 
2567  {
2568  // Modify the trajectory at the end if there is a consistent increase in delta. It
2569  // is called from CheckTraj.
2570  // This needs to be done carefully...
2571 
2572  if(!tcc.useAlg[kHED]) return;
2573  if(tj.StopFlag[1][kBragg]) return;
2574  if(tj.Strategy[kStiffEl]) return;
2575  // Only consider long high momentum.
2576  if(tj.MCSMom < 100) return;
2577  if(tj.Pts.size() < 50) return;
2578 
2579  unsigned short ept = tj.EndPt[1];
2580 
2581  TrajPoint& lastTp = tj.Pts[ept];
2582 
2583  if(lastTp.AngleCode > 1) return;
2584  if(lastTp.FitChi < 1) return;
2585 
2586  unsigned short npts = USHRT_MAX;
2587  float lastDelta = lastTp.Delta;
2588  // check the last 20 points on the trajectory for a systematic increase in Delta and FitChi
2589  for(unsigned short ii = 1; ii < 20; ++ii) {
2590  unsigned short ipt = ept - ii;
2591  TrajPoint& tp = tj.Pts[ipt];
2592  if(tp.Chg == 0) continue;
2593  if(tp.FitChi < 1 || tp.Delta > lastDelta) {
2594  npts = ii;
2595  break;
2596  }
2597  lastDelta = tp.Delta;
2598  } // ii
2599 
2600  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"HED: last point FitChi "<<lastTp.FitChi<<" NTPsFit "<<lastTp.NTPsFit<<" new npts "<<npts;
2601 
2602  // something bad happened
2603  if(npts == USHRT_MAX) return;
2604  // The Tj end has some other problem
2605  if(npts < 4) return;
2606 
2607  // re-fit the end of the trajectory
2608  lastTp.NTPsFit = npts;
2609  FitTraj(slc, tj);
2610  if(tcc.dbgStp) PrintTrajPoint("HED", slc, ept, tj.StepDir, tj.Pass, lastTp);
2611  // update the last points
2612  for(unsigned short ii = 1; ii <= npts; ++ii) {
2613  unsigned short ipt = ept - ii;
2614  TrajPoint& tp = tj.Pts[ipt];
2615  if(tp.Chg == 0) continue;
2616  tp.Dir = tj.Pts[ept].Dir;
2617  tp.Ang = tj.Pts[ept].Ang;
2618  tp.AngErr = tj.Pts[ept].AngErr;
2619  tp.AngleCode = tj.Pts[ept].AngleCode;
2620  // Correct the projected time to the wire
2621  float dw = tp.Pos[0] - tj.Pts[ept].Pos[0];
2622  if(tp.Dir[0] != 0) tp.Pos[1] = tj.Pts[ept].Pos[1] + dw * tp.Dir[1] / tp.Dir[0];
2623  tp.Delta = PointTrajDOCA(slc, tp.HitPos[0], tp.HitPos[1], tp);
2624  tp.DeltaRMS = tj.Pts[ept].DeltaRMS;
2625  tp.NTPsFit = tj.Pts[ept].NTPsFit;
2626  tp.FitChi = tj.Pts[ept].FitChi;
2627  if(tcc.dbgStp) PrintTrajPoint("HED", slc, ipt, tj.StepDir, tj.Pass, tp);
2628  } // ii
2629 
2630  tj.AlgMod[kHED] = true;
2631 
2632  } // HiEndDelta
2633 
2636  {
2637  // Estimate the Delta RMS of the TPs on the end of tj.
2638 
2639  unsigned int lastPt = tj.EndPt[1];
2640  TrajPoint& lastTP = tj.Pts[lastPt];
2641 
2642  if(lastTP.Chg == 0) return;
2643  if(lastPt < 6) return;
2644 
2645  unsigned short ii, ipt, cnt = 0;
2646  float sum = 0;
2647  for(ii = 1; ii < tj.Pts.size(); ++ii) {
2648  ipt = lastPt - ii;
2649  if(ipt > tj.Pts.size() - 1) break;
2650  if(tj.Pts[ipt].Chg == 0) continue;
2651  sum += PointTrajDOCA(slc, tj.Pts[ipt].Pos[0], tj.Pts[ipt].Pos[1], lastTP);
2652  ++cnt;
2653  if(cnt == lastTP.NTPsFit) break;
2654  if(ipt == 0) break;
2655  }
2656  if(cnt < 3) return;
2657  // RMS of Gaussian distribution is ~1.2 x the average
2658  // of a one-sided Gaussian distribution (since Delta is > 0)
2659  lastTP.DeltaRMS = 1.2 * sum / (float)cnt;
2660  if(lastTP.DeltaRMS < 0.02) lastTP.DeltaRMS = 0.02;
2661 
2662  } // UpdateDeltaRMS
2663 
2665  void MaskBadTPs(TCSlice& slc, Trajectory& tj, float const& maxChi)
2666  {
2667  // Remove TPs that have the worst values of delta until the fit chisq < maxChi
2668 
2669  if(!tcc.useAlg[kMaskBadTPs]) return;
2670  //don't use this function for reverse propagation
2671  if(!tcc.useAlg[kRvPrp]) return;
2672 
2673  bool prt = (tcc.dbgStp || tcc.dbgAlg[kMaskBadTPs]);
2674 
2675  if(tj.Pts.size() < 3) {
2676  // mf::LogError("TC")<<"MaskBadTPs: Trajectory ID "<<tj.ID<<" too short to mask hits ";
2677  tj.IsGood = false;
2678  return;
2679  }
2680  unsigned short nit = 0;
2681  TrajPoint& lastTP = tj.Pts[tj.Pts.size() - 1];
2682  while(lastTP.FitChi > maxChi && nit < 3) {
2683  float maxDelta = 0;
2684  unsigned short imBad = USHRT_MAX;
2685  unsigned short cnt = 0;
2686  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
2687  unsigned short ipt = tj.Pts.size() - 1 - ii;
2688  TrajPoint& tp = tj.Pts[ipt];
2689  if(tp.Chg == 0) continue;
2690  if(tp.Delta > maxDelta) {
2691  maxDelta = tp.Delta;
2692  imBad = ipt;
2693  }
2694  ++cnt;
2695  if(cnt == tp.NTPsFit) break;
2696  } // ii
2697  if(imBad == USHRT_MAX) return;
2698  if(prt) mf::LogVerbatim("TC")<<"MaskBadTPs: lastTP.FitChi "<<lastTP.FitChi<<" Mask point "<<imBad;
2699  // mask the point
2700  UnsetUsedHits(slc, tj.Pts[imBad]);
2701  FitTraj(slc, tj);
2702  if(prt) mf::LogVerbatim("TC")<<" after FitTraj "<<lastTP.FitChi;
2703  tj.AlgMod[kMaskBadTPs] = true;
2704  ++nit;
2705  } // lastTP.FItChi > maxChi && nit < 3
2706 
2707  } // MaskBadTPs
2708 
2711  {
2712  // The hits in the TP at the end of the trajectory were masked off. Decide whether to continue stepping with the
2713  // current configuration (true) or whether to stop and possibly try with the next pass settings (false)
2714 
2715  if(!tcc.useAlg[kMaskHits]) return true;
2716 
2717  unsigned short lastPt = tj.Pts.size() - 1;
2718  if(tj.Pts[lastPt].Chg > 0) return true;
2719  unsigned short endPt = tj.EndPt[1];
2720 
2721  // count the number of points w/o used hits and the number with one unused hit
2722  unsigned short nMasked = 0;
2723  unsigned short nOneHit = 0;
2724  unsigned short nOKChg = 0;
2725  unsigned short nOKDelta = 0;
2726  // number of points with Pos > HitPos
2727  unsigned short nPosDelta = 0;
2728  // number of points with Delta increasing vs ipt
2729  unsigned short nDeltaIncreasing = 0;
2730  // Fake this a bit to simplify comparing the counts
2731  float prevDelta = tj.Pts[endPt].Delta;
2732  float maxOKDelta = 10 * tj.Pts[endPt].DeltaRMS;
2733  float maxOKChg = 0;
2734  // find the maximum charge point on the trajectory
2735  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) if(tj.Pts[ipt].Chg > maxOKChg) maxOKChg = tj.Pts[ipt].Chg;
2736  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
2737  unsigned short ipt = tj.Pts.size() - ii;
2738  auto& tp = tj.Pts[ipt];
2739  if(tp.Chg > 0) break;
2740  unsigned short nUnusedHits = 0;
2741  float chg = 0;
2742  for(unsigned short jj = 0; jj < tp.Hits.size(); ++jj) {
2743  unsigned int iht = tp.Hits[jj];
2744  if(slc.slHits[iht].InTraj != 0) continue;
2745  ++nUnusedHits;
2746  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2747  chg += hit.Integral();
2748  } // jj
2749  if(chg < maxOKChg) ++nOKChg;
2750  if(nUnusedHits == 1) ++nOneHit;
2751  if(tp.Delta < maxOKDelta) ++nOKDelta;
2752  // count the number of points with Pos time > HitPos time
2753  if(tp.Pos[1] > tp.HitPos[1]) ++nPosDelta;
2754  // The number of increasing delta points: Note implied absolute value
2755  if(tp.Delta < prevDelta) ++nDeltaIncreasing;
2756  prevDelta = tp.Delta;
2757  ++nMasked;
2758  } // ii
2759 
2760  // determine if the hits are wandering away from the trajectory direction. This will result in
2761  // nPosDelta either being ~0 or ~equal to the number of masked points. nPosDelta should have something
2762  // in between these two extremes if we are stepping through a messy region
2763  bool driftingAway = nMasked > 2 && (nPosDelta == 0 || nPosDelta == nMasked);
2764  // Note that nDeltaIncreasing is always positive
2765  if(driftingAway && nDeltaIncreasing < nMasked - 1) driftingAway = false;
2766 
2767  if(tcc.dbgStp) {
2768  mf::LogVerbatim("TC")<<"MHOK: nMasked "<<nMasked<<" nOneHit "<<nOneHit<<" nOKChg "<<nOKChg<<" nOKDelta "<<nOKDelta<<" nPosDelta "<<nPosDelta<<" nDeltaIncreasing "<<nDeltaIncreasing<<" driftingAway? "<<driftingAway;
2769  }
2770 
2771  if(!driftingAway) {
2772  if(nMasked < 8 || nOneHit < 8) return true;
2773  if(nOKDelta != nMasked) return true;
2774  if(nOKChg != nMasked) return true;
2775  }
2776 
2777  // we would like to reduce the number of fitted points to a minimum and include
2778  // the masked hits, but we can only do that if there are enough points
2779  if(tj.Pts[endPt].NTPsFit <= tcc.minPtsFit[tj.Pass]) {
2780  // stop stepping if we have masked off more points than are in the fit
2781  if(nMasked > tj.Pts[endPt].NTPsFit) return false;
2782  return true;
2783  }
2784  // Reduce the number of points fit and try to include the points
2785  unsigned short newNTPSFit;
2786  if(tj.Pts[endPt].NTPsFit > 2 * tcc.minPtsFit[tj.Pass]) {
2787  newNTPSFit = tj.Pts[endPt].NTPsFit / 2;
2788  } else {
2789  newNTPSFit = tcc.minPtsFit[tj.Pass];
2790  }
2791  for(unsigned ipt = endPt + 1; ipt < tj.Pts.size(); ++ipt) {
2792  TrajPoint& tp = tj.Pts[ipt];
2793  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2794  unsigned int iht = tp.Hits[ii];
2795  if(slc.slHits[iht].InTraj == 0) {
2796  tp.UseHit[ii] = true;
2797  slc.slHits[iht].InTraj = tj.ID;
2798  break;
2799  }
2800  } // ii
2801  DefineHitPos(slc, tp);
2802  SetEndPoints(tj);
2803  tp.NTPsFit = newNTPSFit;
2804  FitTraj(slc, tj);
2805  if(tcc.dbgStp) PrintTrajectory("MHOK", slc, tj, ipt);
2806  } // ipt
2807 
2808  tj.AlgMod[kMaskHits] = true;
2809  UpdateTjChgProperties("MHOK", slc, tj, tcc.dbgStp);
2810  return true;
2811 
2812  } // MaskedHitsOK
2813 
2816  {
2817  // Returns true if there are a number of Tps that were not used in the trajectory because the fit was poor and the
2818  // charge pull is not really high. This
2819 
2820  // don't consider muons
2821  if(tj.PDGCode == 13) return false;
2822  // or long straight Tjs
2823  if(tj.Pts.size() > 40 && tj.MCSMom > 200) return false;
2824 
2825  unsigned short nBadFit = 0;
2826  unsigned short nHiChg = 0;
2827  unsigned short cnt = 0;
2828  for(unsigned short ipt = tj.Pts.size() - 1; ipt > tj.EndPt[1]; --ipt ) {
2829  if(tj.Pts[ipt].FitChi > 2) ++nBadFit;
2830  if(tj.Pts[ipt].ChgPull > 3) ++nHiChg;
2831  ++cnt;
2832  if(cnt == 5) break;
2833  } // ipt
2834 
2835  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"StopIfBadFits: nBadFit "<<nBadFit<<" nHiChg "<<nHiChg;
2836  if(nBadFit > 3 && nHiChg == 0) return true;
2837  return false;
2838 
2839  } // StopIfBadFits
2840 
2842  void GottaKink(TCSlice& slc, Trajectory& tj, unsigned short& killPts)
2843  {
2844  // Checks the last few points on the trajectory and returns with the number of
2845  // points (killPts) that should be killed (aka masked) at the end
2846  // tcc.kinkCuts
2847  // 0 = kink angle cut (radians)
2848  // 1 = kink angle significance cut
2849  // 2 = nPts fit at the end of the tj
2850  // Kink angle cut = tcc.kinkCuts[0] + tcc.kinkCuts[1] * MCSThetaRMS
2851 
2852  killPts = 0;
2853 
2854  // don't apply kink cuts if this looks a high energy electron
2855  if(tj.Strategy[kStiffEl] || tj.Strategy[kStiffMu]) return;
2856 
2857  // decide whether to turn kink checking back on
2858  if(tcc.kinkCuts[0] > 0 && tj.EndPt[1] == 20) {
2859  if(MCSMom(slc, tj, 10, 19) > 50) tj.AlgMod[kNoKinkChk] = false;
2860  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"GottaKink turn kink checking back on? "<<tj.AlgMod[kNoKinkChk]<<" with MCSMom "<<MCSMom(slc, tj, 10, 19);
2861  }
2862  if(tj.AlgMod[kNoKinkChk]) return;
2863 
2864  // we need at least 2 * kinkCuts[2] points with charge to find a kink
2865  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
2866  unsigned short nPtsFit = tcc.kinkCuts[2];
2867  // fit more points if this is a muon
2868  if(tcc.useAlg[kNewStpCuts] && tj.PDGCode == 13) nPtsFit += 2;
2869  if(npwc < 2 * nPtsFit) return;
2870  unsigned short lastPt = tj.EndPt[1];
2871  if(tj.Pts[lastPt].Chg == 0) return;
2872 
2873  // MCSThetaRMS is the scattering angle for the entire length of the trajectory. Convert
2874  // this to the scattering angle for one WSE unit
2875  float thetaRMS = MCSThetaRMS(slc, tj, tj.EndPt[0], tj.EndPt[1]) / sqrt(TrajPointSeparation(tj.Pts[tj.EndPt[0]], tj.Pts[lastPt]));
2876  float kinkAngCut = tcc.kinkCuts[0] + tcc.kinkCuts[1] * thetaRMS;
2877  // relax this a bit when doing RevProp
2878  if(tj.AlgMod[kRvPrp]) kinkAngCut *= 1.3;
2879 
2880  // modify for stopping tracks
2881  if(tj.Strategy[kSlowing]) {
2882  nPtsFit = 3;
2883  kinkAngCut *= 3;
2884  }
2885 
2886  // find the point where a kink is expected and fit the points after that point
2887  unsigned short kinkPt = 0;
2888  unsigned short cnt = 0;
2889  float endChg = 0;
2890  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
2891  unsigned short ipt = tj.EndPt[1] - ii - 1;
2892  // stay away from the starting points which may be skewed if this is a
2893  // stopping track
2894  if(ipt <= tj.EndPt[0] + 2) break;
2895  if(tj.Pts[ipt].Chg <= 0) continue;
2896  ++cnt;
2897  endChg += tj.Pts[ipt].Chg;
2898  if(cnt == nPtsFit) {
2899  kinkPt = ipt;
2900  break;
2901  }
2902  } // ii
2903  if(kinkPt == 0 || cnt == 0) return;
2904  // fit the last nPtsFit points
2905  unsigned short fitDir = -1;
2906  TrajPoint tpFit;
2907  FitTraj(slc, tj, lastPt, nPtsFit, fitDir, tpFit);
2908  float dang = std::abs(tj.Pts[kinkPt].Ang - tpFit.Ang);
2909  // Calculate the average charge of the last ~3 points
2910  endChg /= cnt;
2911  float err = tj.Pts[kinkPt].AngErr;
2912  if(tpFit.AngErr > err) err = tpFit.AngErr;
2913  if(tj.Strategy[kSlowing]) err *= 2;
2914  float kinkSig = dang / err;
2915  float endChgAsym = (endChg - tj.Pts[kinkPt].AveChg) / (endChg + tj.Pts[kinkPt].AveChg);
2916  if(tcc.dbgStp) {
2917  mf::LogVerbatim myprt("TC");
2918  myprt<<"GottaKink kinkPt "<<PrintPos(slc, tj.Pts[kinkPt]);
2919  myprt<<std::setprecision(3);
2920  myprt<<" Ang before "<<tj.Pts[kinkPt].Ang<<" Err "<<tj.Pts[kinkPt].AngErr;
2921  myprt<<" AveChg "<<(int)tj.Pts[kinkPt].AveChg;
2922  myprt<<" Ang after "<<tpFit.Ang<<" Err "<<tpFit.AngErr;
2923  myprt<<" endChg "<<endChg;
2924  myprt<<" dang "<<dang;
2925  myprt<<" kinkAngCut "<<kinkAngCut;
2926  myprt<<" kinkSig "<<kinkSig;
2927  myprt<<" endChgAsym "<<endChgAsym;
2928  } // dbgStp
2929 
2930  if(tcc.useAlg[kNewStpCuts] && npwc < 20) {
2931  // improvements(?) for short tjs where the kink angle is a bit low but the kink
2932  // angle significance is high
2933  bool foundKink = (dang > 0.8 * kinkAngCut && kinkSig > 5);
2934  if(tcc.dbgStp && foundKink) {
2935  } // debug
2936  if(foundKink) {
2937  killPts = nPtsFit;
2938  tj.StopFlag[1][kAtKink] = true;
2939  tj.AlgMod[kNewStpCuts] = true;
2940  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Found a short Tj kink";
2941  return;
2942  }
2943  } // kNewStpCuts and short tj
2944 
2945  if(tcc.useAlg[kNewStpCuts] && tj.PDGCode == 13 && dang > kinkAngCut) {
2946  // New muon cuts. There is probably a delta-ray at the end if there is a kink
2947  // that is not really large and the end charge is high - Not a kink.
2948  // chgAsym = 0.2 corresponds to an average charge after the kink points that is
2949  // 50% larger than the charge before the kink point
2950  if(dang < 2 * kinkAngCut && endChgAsym > 0.2) return;
2951  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Found a muon Tj kink. Calling it not-a-kink";
2952  killPts = nPtsFit;
2953  tj.StopFlag[1][kAtKink] = true;
2954  tj.AlgMod[kNewStpCuts] = true;
2955  return;
2956  } // kNewStpCuts and muon with a kink
2957 
2958  if(dang > kinkAngCut) {
2959  killPts = nPtsFit;
2960  tj.StopFlag[1][kAtKink] = true;
2961  }
2962 
2963  if(killPts > 0) {
2964  // See if we are tracking a low momentum particle in which case we should just
2965  // turn off kink checking
2966  if(tcc.useAlg[kNoKinkChk] && tj.EndPt[1] < 20) {
2967  // Find MCSMom if it hasn't been done
2968  if(tj.MCSMom < 0) tj.MCSMom = MCSMom(slc, tj);
2969  if(tj.MCSMom < 50) {
2970  killPts = 0;
2971  tj.StopFlag[1][kAtKink] = false;
2972  tj.AlgMod[kNoKinkChk] = true;
2973  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"GottaKink turning off kink checking. MCSMom "<<tj.MCSMom;
2974  }
2975  } // turn off kink check
2976  // Don't stop if the last few points had high charge pull and we are tracking a muon, but do mask off the hits
2977  if(killPts > 0 && tj.PDGCode == 13 && tj.Pts[lastPt].ChgPull > 2 && tj.Pts[lastPt-1].ChgPull > 2) tj.StopFlag[1][kAtKink] = false;
2978  // Don't keep stepping or mask off any TPs if we hit a kink while doing RevProp
2979  if(tj.AlgMod[kRvPrp]) killPts = 0;
2980  }
2981 
2982  if(tcc.dbgStp && tj.StopFlag[1][kAtKink]) mf::LogVerbatim("TC")<<"GottaKink killPts "<<killPts;
2983 // if(tcc.dbgStp) mf::LogVerbatim("TC")<<"GottaKink "<<kinkPt<<" Pos "<<PrintPos(slc, 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;
2984 
2985  } // GottaKink
2986 
2989  {
2990  // Update the parameters at the beginning of the trajectory. The first
2991  // points may not belong to this trajectory since they were added when there was
2992  // little information. This information may be updated later if ReversePropagate is used
2993 
2994  if(!tcc.useAlg[kFixBegin]) return;
2995  if(tj.AlgMod[kJunkTj]) return;
2996 
2997  // don't do anything if this tj has been modified by ReversePropagate
2998  if(tj.AlgMod[kRvPrp]) return;
2999 
3000  // don't bother with really short tjs
3001  if(tj.Pts.size() < 3) return;
3002 
3003  unsigned short atPt = tj.EndPt[1];
3004  unsigned short maxPtsFit = 0;
3005  for(unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
3006  if(tj.Pts[ipt].Chg == 0) continue;
3007  if(tj.Pts[ipt].NTPsFit > maxPtsFit) {
3008  maxPtsFit = tj.Pts[ipt].NTPsFit;
3009  atPt = ipt;
3010  // no reason to continue if there are a good number of points fitted
3011  if(maxPtsFit > 20) break;
3012  }
3013  } // ipt
3014  // find the first point that is in this fit
3015  unsigned short firstPtFit = tj.EndPt[0];
3016  unsigned short cnt = 0;
3017  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
3018  if(ii > atPt) break;
3019  unsigned short ipt = atPt - ii;
3020  if(tj.Pts[ipt].Chg == 0) continue;
3021  ++cnt;
3022  if(cnt == maxPtsFit) {
3023  firstPtFit = ipt;
3024  break;
3025  } // full count
3026  } // ii
3027 
3028  bool needsRevProp = firstPtFit > 3;
3029  unsigned short nPtsLeft = NumPtsWithCharge(slc, tj, false) - firstPtFit;
3030  if(tcc.useAlg[kNewStpCuts] && needsRevProp) needsRevProp = (nPtsLeft > 5);
3031  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"FTB: firstPtFit "<<PrintPos(slc, tj.Pts[firstPtFit].Pos)<<" atPt "<<PrintPos(slc, tj.Pts[atPt].Pos)<<" nPts left after trimming "<<nPtsLeft;
3032 
3033  if(!needsRevProp) {
3034  // check one wire on the other side of EndPt[0] to see if there are hits that are available which could
3035  // be picked up by reverse propagation
3036  TrajPoint tp = tj.Pts[0];
3037  tp.Hits.clear();
3038  tp.UseHit.reset();
3039  // Move the TP "backwards"
3040  double stepSize = tcc.VLAStepSize;
3041  if(tp.AngleCode < 2) stepSize = std::abs(1/tp.Dir[0]);
3042  tp.Pos[0] -= tp.Dir[0] * stepSize * tj.StepDir;
3043  tp.Pos[1] -= tp.Dir[1] * stepSize * tj.StepDir;
3044  if(tcc.useAlg[kNewStpCuts]) {
3045  // launch RevProp if this wire is dead
3046  unsigned int wire = std::nearbyint(tp.Pos[0]);
3047  unsigned short plane = DecodeCTP(tp.CTP).Plane;
3048  needsRevProp = (wire < slc.nWires[plane] && slc.wireHitRange[plane][wire].first == -1);
3049  if(tcc.dbgStp && needsRevProp) mf::LogVerbatim("TC")<<"FTB: Previous wire "<<wire<<" is dead. Call ReversePropagate";
3050  } // NewStpCuts
3051  if(!needsRevProp) {
3052  // check for hits on a not-dead wire
3053  float maxDelta = 3 * tp.DeltaRMS;
3054  if(FindCloseHits(slc, tp, maxDelta, kUnusedHits) && !tp.Hits.empty()) {
3055  needsRevProp = true;
3056  if(tcc.dbgStp) {
3057  mf::LogVerbatim("TC")<<"FTB: Close unused hits found near EndPt[0] "<<tp.Hits.size()<<". Call ReversePropagate";
3058  PrintTrajPoint("FTB", slc, 0, tj.StepDir, tj.Pass, tp);
3059  }
3060  }
3061  } // !needsRevProp
3062  } // !needsRevProp
3063 
3064  if(tcc.dbgStp) {
3065  mf::LogVerbatim("TC")<<"FTB: maxPtsFit "<<maxPtsFit<<" at point "<<atPt<<" firstPtFit "<<firstPtFit<<" Needs ReversePropagate? "<<needsRevProp;
3066  }
3067 
3068  if(tcc.useAlg[kFTBRvProp] && needsRevProp) {
3069  // lop off the points before firstPtFit and reverse propagate
3070  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" clobber TPs "<<PrintPos(slc, tj.Pts[0])<<" to "<<PrintPos(slc, tj.Pts[firstPtFit])<<". Call TrimEndPts then ReversePropagate ";
3071  // first save the first TP on this trajectory. We will try to re-use it if
3072  // it isn't used during reverse propagation
3073  if(tcc.useAlg[kNewStpCuts]) seeds.push_back(tj.Pts[0]);
3074  for(unsigned short ipt = 0; ipt <= firstPtFit; ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
3075  SetEndPoints(tj);
3076  tj.AlgMod[kFTBRvProp] = true;
3077  // Check for quality and trim if necessary before reverse propagation
3078  TrimEndPts("RPi", slc, tj, tcc.qualityCuts, tcc.dbgStp);
3079  if(tj.AlgMod[kKilled]) {
3080  tj.IsGood = false;
3081  return;
3082  }
3083  ReversePropagate(slc, tj);
3084  ChkStopEndPts(slc, tj, tcc.dbgStp);
3085  }
3086  // Clean up the first points if no reverse propagation was done
3087  if(!tj.AlgMod[kRvPrp]) FixTrajBegin(slc, tj, atPt);
3088 
3089  } // FixTrajBegin
3090 
3092  void FixTrajBegin(TCSlice& slc, Trajectory& tj, unsigned short atPt)
3093  {
3094  // Update the parameters at the beginning of the trajectory starting at point atPt
3095 
3096  if(!tcc.useAlg[kFixBegin]) return;
3097  // ignore short trajectories
3098  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
3099  if(npwc < 6) return;
3100  // ignore somewhat longer trajectories that are curly
3101  if(npwc < 10 && tj.MCSMom < 100) return;
3102  // ignore shower-like trajectories
3103  if(tj.PDGCode == 11) return;
3104  // ignore junk trajectories
3105  if(tj.AlgMod[kJunkTj]) return;
3106  // ignore stopping trajectories
3107  if(tj.StopFlag[0][kBragg]) return;
3108 
3109 
3110  unsigned short firstPt = tj.EndPt[0];
3111 
3112  if(atPt == tj.EndPt[0]) return;
3113 
3114  // Default is to use DeltaRMS of the last point on the Tj
3115  float maxDelta = 4 * tj.Pts[tj.EndPt[1]].DeltaRMS;
3116  if(tcc.useAlg[kNewStpCuts]) {
3117  // 10/2/2018 BB Change requirement
3118  // Find the max DeltaRMS of points from atPt to EndPt[1]
3119  float maxDeltaRMS = 0;
3120  for(unsigned short ipt = atPt; ipt <= tj.EndPt[1]; ++ipt) {
3121  if(tj.Pts[ipt].DeltaRMS > maxDeltaRMS) maxDeltaRMS = tj.Pts[ipt].DeltaRMS;
3122  } // ipt
3123  maxDelta = 3 * maxDeltaRMS;
3124  } // kNewStpCuts
3125 
3126  if(tcc.dbgStp) {
3127  mf::LogVerbatim("TC")<<"FixTrajBegin: atPt "<<atPt<<" firstPt "<<firstPt<<" Stops at end 0? "<<PrintStopFlag(tj, 0)<<" start vertex "<<tj.VtxID[0]<<" maxDelta "<<maxDelta;
3128  }
3129 
3130  // update the trajectory for all the points up to atPt
3131  // assume that we will use all of these points
3132  bool maskedPts = false;
3133  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
3134  if(ii > atPt) break;
3135  unsigned int ipt = atPt - ii;
3136  TrajPoint& tp = tj.Pts[ipt];
3137  tp.Dir = tj.Pts[atPt].Dir;
3138  tp.Ang = tj.Pts[atPt].Ang;
3139  tp.AngErr = tj.Pts[atPt].AngErr;
3140  tp.AngleCode = tj.Pts[atPt].AngleCode;
3141  // Correct the projected time to the wire
3142  float dw = tp.Pos[0] - tj.Pts[atPt].Pos[0];
3143  if(tp.Dir[0] != 0) tp.Pos[1] = tj.Pts[atPt].Pos[1] + dw * tp.Dir[1] / tp.Dir[0];
3144  tj.Pts[ipt].Delta = PointTrajDOCA(slc, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tj.Pts[ipt]);
3145  tj.Pts[ipt].DeltaRMS = tj.Pts[atPt].DeltaRMS;
3146  tj.Pts[ipt].NTPsFit = tj.Pts[atPt].NTPsFit;
3147  tj.Pts[ipt].FitChi = tj.Pts[atPt].FitChi;
3148  tj.Pts[ipt].AveChg = tj.Pts[atPt].AveChg;
3149  tj.Pts[ipt].ChgPull = (tj.Pts[ipt].Chg / tj.AveChg - 1) / tj.ChgRMS;
3150  bool maskThisPt = (tj.Pts[ipt].Delta > maxDelta);
3151  if(maskThisPt) maskedPts = true;
3152  if(tcc.useAlg[kNewStpCuts]) {
3153  // 10/1/18 BB only mask off the bad point. Not all of them to the end
3154  if(maskThisPt) {
3155  UnsetUsedHits(slc, tp);
3156  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" mask off "<<PrintPos(slc, tj.Pts[ipt].Pos)<<" "<<tj.Pts[ipt].Delta;
3157  } // maskThisPt
3158  } else {
3159  // old cuts - mask off all points to the beginning
3160  if(maskedPts) {
3161  UnsetUsedHits(slc, tp);
3162  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" mask off "<<PrintPos(slc, tj.Pts[ipt].Pos)<<" "<<tj.Pts[ipt].Delta;
3163  } // maskedPts
3164  } // old cuts
3165  if(ipt == 0) break;
3166  } // ii
3167  if(maskedPts) SetEndPoints(tj);
3168  tj.AlgMod[kFixBegin] = true;
3169 
3170  } // FixTrajBegin
3171 
3173  void FixTrajEnd(TCSlice& slc, Trajectory& tj, unsigned short atPt)
3174  {
3175  // Update the parameters at the end of the trajectory starting at point atPt
3176 
3177  if(!tcc.useAlg[kFixEnd]) return;
3178  // ignore short trajectories
3179  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
3180  if(npwc < 6) return;
3181  // ignore somewhat longer trajectories that are curly
3182  if(npwc < 10 && tj.MCSMom < 100) return;
3183  // ignore shower-like trajectories
3184  if(tj.PDGCode == 11) return;
3185  // ignore junk trajectories
3186  if(tj.AlgMod[kJunkTj]) return;
3187  // ingore stopping trajectories
3188  if(tj.StopFlag[1][kBragg]) return;
3189 
3190  if(tcc.dbgStp) {
3191  mf::LogVerbatim("TC")<<"FixTrajEnd: atPt "<<atPt;
3192  }
3193 
3194  if(atPt == tj.EndPt[1]) return;
3195 
3196  // update the trajectory for all the intervening points
3197  for(unsigned short ipt = atPt + 1; ipt <= tj.EndPt[1]; ++ipt) {
3198  TrajPoint& tp = tj.Pts[ipt];
3199  tp.Dir = tj.Pts[atPt].Dir;
3200  tp.Ang = tj.Pts[atPt].Ang;
3201  tp.AngErr = tj.Pts[atPt].AngErr;
3202  tp.AngleCode = tj.Pts[atPt].AngleCode;
3203  // Correct the projected time to the wire
3204  float dw = tp.Pos[0] - tj.Pts[atPt].Pos[0];
3205  if(tp.Dir[0] != 0) tp.Pos[1] = tj.Pts[atPt].Pos[1] + dw * tp.Dir[1] / tp.Dir[0];
3206  tj.Pts[ipt].Delta = PointTrajDOCA(slc, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tj.Pts[ipt]);
3207  tj.Pts[ipt].DeltaRMS = tj.Pts[atPt].DeltaRMS;
3208  tj.Pts[ipt].NTPsFit = tj.Pts[atPt].NTPsFit;
3209  tj.Pts[ipt].FitChi = tj.Pts[atPt].FitChi;
3210  tj.Pts[ipt].AveChg = tj.Pts[atPt].AveChg;
3211  tj.Pts[ipt].ChgPull = (tj.Pts[ipt].Chg / tj.AveChg - 1) / tj.ChgRMS;
3212  if(tcc.dbgStp) {
3213  PrintTrajectory("FTE", slc, tj, ipt);
3214  }
3215  } // ipt
3216  tj.AlgMod[kFixEnd] = true;
3217 
3218  } // FixTrajEnd
3219 
3221  bool IsGhost(TCSlice& slc, Trajectory& tj)
3222  {
3223  // Sees if trajectory tj shares many hits with another trajectory and if so merges them.
3224 
3225  if(!tcc.useAlg[kUseGhostHits]) return false;
3226  // ensure that tj is not a saved trajectory
3227  if(tj.ID > 0) return true;
3228  // or an already killed trajectory
3229  if(tj.AlgMod[kKilled]) return true;
3230  if(tj.Pts.size() < 3) return false;
3231  if(tj.Strategy[kStiffEl]) return false;
3232 
3233  // vectors of traj IDs, and the occurrence count
3234  std::vector<int> tID;
3235  std::vector<unsigned short> tCnt;
3236 
3237  unsigned short hitCnt = 0;
3238  unsigned short nAvailable = 0;
3239  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3240  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3241  // ignore hits used by this trajectory
3242  if(tj.Pts[ipt].UseHit[ii]) {
3243  ++hitCnt;
3244  continue;
3245  }
3246  unsigned int iht = tj.Pts[ipt].Hits[ii];
3247  if(slc.slHits[iht].InTraj > 0 && (unsigned int)slc.slHits[iht].InTraj <= slc.tjs.size()) {
3248  int tjid = slc.slHits[iht].InTraj;
3249  unsigned short indx;
3250  for(indx = 0; indx < tID.size(); ++indx) if(tID[indx] == tjid) break;
3251  if(indx == tID.size()) {
3252  tID.push_back(tjid);
3253  tCnt.push_back(1);
3254  } else {
3255  ++tCnt[indx];
3256  }
3257  } else {
3258  ++nAvailable;
3259  }
3260  } // ii
3261  } // ipt
3262 
3263  // Call it a ghost if > 1/3 of the hits are used by another trajectory
3264  hitCnt /= 3;
3265  int oldTjID = INT_MAX;
3266 
3267  if(tcc.dbgStp) {
3268  mf::LogVerbatim myprt("TC");
3269  myprt<<"IsGhost tj hits size cut "<<hitCnt<<" tID_tCnt";
3270  for(unsigned short ii = 0; ii < tCnt.size(); ++ii) myprt<<" "<<tID[ii]<<"_"<<tCnt[ii];
3271  myprt<<"\nAvailable hits "<<nAvailable;
3272  } // prt
3273 
3274  for(unsigned short ii = 0; ii < tCnt.size(); ++ii) {
3275  if(tCnt[ii] > hitCnt) {
3276  oldTjID = tID[ii];
3277  hitCnt = tCnt[ii];
3278  }
3279  } // ii
3280  if(oldTjID == INT_MAX) return false;
3281  int oldTjIndex = oldTjID - 1;
3282 
3283  // See if this looks like a short delta-ray on a long muon
3284  Trajectory& oTj = slc.tjs[oldTjIndex];
3285  if(oTj.PDGCode == 13 && hitCnt < 0.1 * oTj.Pts.size()) return false;
3286 
3287  // See if there are gaps in this trajectory indicating that it is really a ghost and not
3288  // just a crossing trajectory
3289  // find the range of wires spanned by oTj
3290  int wire0 = INT_MAX;
3291  int wire1 = 0;
3292  for(auto& otp : oTj.Pts) {
3293  int wire = std::nearbyint(otp.Pos[0]);
3294  if(wire < wire0) wire0 = wire;
3295  if(wire > wire1) wire1 = wire;
3296  } // tp
3297 
3298  int nwires = wire1 - wire0 + 1;
3299  std::vector<float> oTjPos1(nwires, -1);
3300  unsigned short nMissedWires = 0;
3301  for(unsigned short ipt = oTj.EndPt[0]; ipt <= oTj.EndPt[1]; ++ipt) {
3302  if(oTj.Pts[ipt].Chg == 0) continue;
3303  int wire = std::nearbyint(oTj.Pts[ipt].Pos[0]);
3304  int indx = wire - wire0;
3305  if(indx < 0 || indx > nwires - 1) continue;
3306  oTjPos1[indx] = oTj.Pts[ipt].Pos[1];
3307  ++nMissedWires;
3308  } // ipt
3309  // count the number of ghost TPs
3310  unsigned short ngh = 0;
3311  // and the number with Delta > 0 relative to oTj
3312  unsigned short nghPlus = 0;
3313  // keep track of the first point and last point appearance of oTj
3314  unsigned short firstPtInoTj = USHRT_MAX;
3315  unsigned short lastPtInoTj = 0;
3316  TrajPoint tp = tj.Pts[tj.EndPt[0]];
3317  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3318  if(tj.Pts[ipt].Chg > 0) {
3319  tp = tj.Pts[ipt];
3320  continue;
3321  }
3322  int wire = std::nearbyint(tj.Pts[ipt].Pos[0]);
3323  int indx = wire - wire0;
3324  if(indx < 0 || indx > nwires - 1) continue;
3325  if(oTjPos1[indx] > 0) {
3326  // ensure that the hits in this tp are used in oTj
3327  bool HitInoTj = false;
3328  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3329  unsigned int iht = tj.Pts[ipt].Hits[ii];
3330  if(slc.slHits[iht].InTraj == oldTjID) HitInoTj = true;
3331  } // ii
3332  if(HitInoTj) {
3333  ++ngh;
3334  MoveTPToWire(tp, tj.Pts[ipt].Pos[0]);
3335  if(tp.Pos[1] > oTjPos1[indx]) ++nghPlus;
3336  if(firstPtInoTj == USHRT_MAX) firstPtInoTj = ipt;
3337  lastPtInoTj = ipt;
3338  }
3339  } // oTjHasChg[indx]
3340  } // ipt
3341 
3342  if(tcc.dbgStp) 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;
3343 
3344  if(ngh < 0.2 * nMissedWires) return false;
3345  if(firstPtInoTj > lastPtInoTj) return false;
3346 
3347  // require all of the tj TPs to be on either the + or - side of the oTj trajectory
3348  if(!(nghPlus > 0.8 * ngh || nghPlus < 0.2 * ngh) ) return false;
3349 
3350  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Trajectory is a ghost of "<<oldTjID<<" first point in oTj "<<firstPtInoTj<<" last point "<<lastPtInoTj;
3351 
3352  // unset all of the shared hits
3353  for(unsigned short ipt = firstPtInoTj; ipt <= lastPtInoTj; ++ipt) {
3354  if(tj.Pts[ipt].Chg == 0) continue;
3355  UnsetUsedHits(slc, tj.Pts[ipt]);
3356  if(tcc.dbgStp) PrintTrajectory("IG", slc, tj, ipt);
3357  }
3358  // see how many points are left at the end
3359  ngh = 0;
3360  for(unsigned short ipt = lastPtInoTj; ipt <= tj.Pts.size(); ++ipt) {
3361  if(tj.Pts[ipt].Chg > 0) ++ngh;
3362  } // ipt
3363  // clobber those too?
3364  if(ngh > 0 && ngh < tcc.minPts[tj.Pass]) {
3365  for(unsigned short ipt = lastPtInoTj; ipt <= tj.EndPt[1]; ++ipt) {
3366  if(tj.Pts[ipt].Chg > 0) UnsetUsedHits(slc, tj.Pts[ipt]);
3367  } // ipt
3368  }
3369  SetEndPoints(tj);
3370  tj.Pts.resize(tj.EndPt[1] + 1);
3371  slc.tjs[oldTjIndex].AlgMod[kUseGhostHits] = true;
3372  TrimEndPts("IG", slc, tj, tcc.qualityCuts, tcc.dbgStp);
3373  if(tj.AlgMod[kKilled]) {
3374  tj.IsGood = false;
3375  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" Failed quality cuts";
3376  return true;
3377  }
3378  tj.MCSMom = MCSMom(slc, tj);
3379  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" New tj size "<<tj.Pts.size();
3380  return true;
3381 
3382  } // IsGhost
3383 
3385  bool IsGhost(TCSlice& slc, std::vector<unsigned int>& tHits)
3386  {
3387  // Called by FindJunkTraj to see if the passed hits are close to an existing
3388  // trajectory and if so, they will be used in that other trajectory
3389 
3390  if(!tcc.useAlg[kUseGhostHits]) return false;
3391 
3392  if(tHits.size() < 2) return false;
3393 
3394  bool prt = (tcc.dbgStp || tcc.dbgAlg[kUseGhostHits]);
3395 
3396  // find all nearby hits
3397  std::vector<unsigned int> hitsInMuliplet, nearbyHits;
3398  for(auto iht : tHits) {
3399  GetHitMultiplet(slc, iht, hitsInMuliplet);
3400  // prevent double counting
3401  for(auto mht : hitsInMuliplet) {
3402  if(std::find(nearbyHits.begin(), nearbyHits.end(), mht) == nearbyHits.end()) {
3403  nearbyHits.push_back(mht);
3404  }
3405  } // mht
3406  } // iht
3407 
3408  // vectors of traj IDs, and the occurrence count
3409  std::vector<unsigned int> tID, tCnt;
3410  for(auto iht : nearbyHits) {
3411  if(slc.slHits[iht].InTraj <= 0) continue;
3412  unsigned int tid = slc.slHits[iht].InTraj;
3413  unsigned short indx = 0;
3414  for(indx = 0; indx < tID.size(); ++indx) if(tID[indx] == tid) break;
3415  if(indx == tID.size()) {
3416  tID.push_back(tid);
3417  tCnt.push_back(1);
3418  } else {
3419  ++tCnt[indx];
3420  }
3421  } // iht
3422  if(tCnt.empty()) return false;
3423 
3424  // Call it a ghost if > 50% of the hits are used by another trajectory
3425  unsigned short tCut = 0.5 * tHits.size();
3426  int tid = INT_MAX;
3427 
3428  if(prt) {
3429  mf::LogVerbatim myprt("TC");
3430  myprt<<"IsGhost tHits size "<<tHits.size()<<" cut fraction "<<tCut<<" tID_tCnt";
3431  for(unsigned short ii = 0; ii < tCnt.size(); ++ii) myprt<<" "<<tID[ii]<<"_"<<tCnt[ii];
3432  } // prt
3433 
3434  for(unsigned short ii = 0; ii < tCnt.size(); ++ii) {
3435  if(tCnt[ii] > tCut) {
3436  tid = tID[ii];
3437  break;
3438  }
3439  } // ii
3440  if(tid > (int)slc.tjs.size()) return false;
3441 
3442  if(prt) mf::LogVerbatim("TC")<<" is ghost of trajectory "<<tid;
3443 
3444  // Use all hits in tHits that are found in itj
3445  for(auto& tp : slc.tjs[tid - 1].Pts) {
3446  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3447  unsigned int iht = tp.Hits[ii];
3448  if(slc.slHits[iht].InTraj != 0) continue;
3449  for(unsigned short jj = 0; jj < tHits.size(); ++jj) {
3450  unsigned int tht = tHits[jj];
3451  if(tht != iht) continue;
3452  tp.UseHit[ii] = true;
3453  slc.slHits[iht].InTraj = tid;
3454  break;
3455  } // jj
3456  } // ii
3457  } // tp
3458  slc.tjs[tid - 1].AlgMod[kUseGhostHits] = true;
3459  return true;
3460 
3461  } // IsGhost
3462 
3464  void EndMerge(TCSlice& slc, CTP_t inCTP, bool lastPass)
3465  {
3466  // Merges trajectories end-to-end or makes vertices. Does a more careful check on the last pass
3467 
3468  if(slc.tjs.size() < 2) return;
3469  if(!tcc.useAlg[kMerge]) return;
3470 
3471  bool prt = (tcc.dbgMrg && tcc.dbgSlc && inCTP == debug.CTP);
3472  if(prt) mf::LogVerbatim("TC")<<"inside EndMerge slice "<<slices.size()-1<<" inCTP "<<inCTP<<" nTjs "<<slc.tjs.size()<<" lastPass? "<<lastPass;
3473 
3474  // Ensure that all tjs are in the same order
3475  short tccStepDir = 1;
3476  if(!tcc.modes[kStepDir]) tccStepDir = -1;
3477  for(auto& tj : slc.tjs) {
3478  if(tj.AlgMod[kKilled]) continue;
3479  if(tj.CTP != inCTP) continue;
3480  if(tj.StepDir != tccStepDir) ReverseTraj(slc, tj);
3481  } // tj
3482 
3483  unsigned short maxShortTjLen = tcc.vtx2DCuts[0];
3484 
3485  // temp vector for checking the fraction of hits near a merge point
3486  std::vector<int> tjlist(2);
3487 
3488  float minChgRMS = 0.5 * (tcc.chargeCuts[1] + tcc.chargeCuts[2]);
3489 
3490  // iterate whenever a merge occurs since tjs will change. This is not necessary
3491  // when a vertex is created however.
3492  bool iterate = true;
3493  while(iterate) {
3494  iterate = false;
3495  for(unsigned int it1 = 0; it1 < slc.tjs.size(); ++it1) {
3496  auto& tj1 = slc.tjs[it1];
3497  if(tj1.AlgMod[kKilled]) continue;
3498  if(tj1.CTP != inCTP) continue;
3499  // don't try to merge high energy electrons
3500  if(tj1.PDGCode == 111) continue;
3501  for(unsigned short end1 = 0; end1 < 2; ++end1) {
3502  // no merge if there is a vertex at the end
3503  if(tj1.VtxID[end1] > 0) continue;
3504  // make a copy of tp1 so we can mess with it
3505  TrajPoint tp1 = tj1.Pts[tj1.EndPt[end1]];
3506  // do a local fit on the lastpass only using the last 3 points
3507  if(lastPass && tp1.NTPsFit > 3) {
3508  // make a local copy of the tj
3509  auto ttj = slc.tjs[it1];
3510  auto& lastTP = ttj.Pts[ttj.EndPt[end1]];
3511  // fit the last 3 points
3512  lastTP.NTPsFit = 3;
3513  FitTraj(slc, ttj);
3514  tp1 = ttj.Pts[ttj.EndPt[end1]];
3515  } // last pass
3516  bool isVLA = (tp1.AngleCode == 2);
3517  float bestFOM = 5;
3518  if(isVLA) bestFOM = 20;
3519  float bestDOCA;
3520  unsigned int imbest = INT_MAX;
3521  for(unsigned int it2 = 0; it2 < slc.tjs.size(); ++it2) {
3522  if(it1 == it2) continue;
3523  auto& tj2 = slc.tjs[it2];
3524  // check for consistent direction
3525  if(tj1.StepDir != tj2.StepDir) continue;
3526  if(tj2.AlgMod[kKilled]) continue;
3527  if(tj2.CTP != inCTP) continue;
3528  // don't try to merge high energy electrons
3529  if(tj2.PDGCode == 111) continue;
3530  float olf = OverlapFraction(slc, tj1, tj2);
3531  if(olf > 0.25) continue;
3532  unsigned short end2 = 1 - end1;
3533  // check for a vertex at this end
3534  if(tj2.VtxID[end2] > 0) continue;
3535  TrajPoint& tp2 = tj2.Pts[tj2.EndPt[end2]];
3536  TrajPoint& tp2OtherEnd = tj2.Pts[tj2.EndPt[end1]];
3537  // ensure that the other end isn't closer
3538  if(std::abs(tp2OtherEnd.Pos[0] - tp1.Pos[0]) < std::abs(tp2.Pos[0] - tp1.Pos[0])) continue;
3539  // ensure that the order is correct
3540  if(tj1.StepDir > 0) {
3541  if(tp2.Pos[0] < tp1.Pos[0] - 2) continue;
3542  } else {
3543  if(tp2.Pos[0] > tp1.Pos[0] + 2) continue;
3544  }
3545  // ensure that there is a signal on most of the wires between these points
3546  if(!SignalBetween(slc, tp1, tp2, 0.8)) {
3547  continue;
3548  }
3549  // Find the distance of closest approach for small angle merging
3550  // Inflate the doca cut if we are bridging a block of dead wires
3551  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
3552  float doca = 15;
3553  if(isVLA) {
3554  // compare the minimum separation between Large Angle trajectories using a generous cut
3555  unsigned short ipt1, ipt2;
3556  TrajTrajDOCA(slc, tj1, tj2, ipt1, ipt2, doca);
3557  // if(prt) mf::LogVerbatim("TC")<<" isVLA check ipt1 "<<ipt1<<" ipt2 "<<ipt2<<" doca "<<doca;
3558  } else {
3559  // small angle
3560  doca = PointTrajDOCA(slc, tp1.Pos[0], tp1.Pos[1], tp2);
3561  }
3562  float fom = dang * doca;
3563  if(fom < bestFOM) {
3564  bestFOM = fom;
3565  bestDOCA = doca;
3566  imbest = it2;
3567  }
3568  } // it2
3569  // No merge/vertex candidates
3570  if(imbest == INT_MAX) continue;
3571 
3572  // Make angle adjustments to tp1.
3573  unsigned int it2 = imbest;
3574  auto& tj2 = slc.tjs[imbest];
3575  unsigned short end2 = 1 - end1;
3576  bool loMCSMom = (tj1.MCSMom + tj2.MCSMom) < 150;
3577  // Don't use the angle at the end Pt for high momentum long trajectories in case there is a little kink at the end
3578  if(tj1.Pts.size() > 50 && tj1.MCSMom > 100) {
3579  if(end1 == 0) {
3580  tp1.Ang = tj1.Pts[tj1.EndPt[0] + 2].Ang;
3581  } else {
3582  tp1.Ang = tj1.Pts[tj1.EndPt[1] - 2].Ang;
3583  }
3584  } else if(loMCSMom) {
3585  // Low momentum - calculate the angle using the two Pts at the end
3586  unsigned short pt1, pt2;
3587  if(end1 == 0) {
3588  pt1 = tj1.EndPt[0];
3589  pt2 = pt1 + 1;
3590  } else {
3591  pt2 = tj1.EndPt[1];
3592  pt1 = pt2 - 1;
3593  }
3594  TrajPoint tpdir;
3595  if(MakeBareTrajPoint(slc, tj1.Pts[pt1], tj1.Pts[pt2], tpdir)) tp1.Ang = tpdir.Ang;
3596  } // low MCSMom
3597  // Now do the same for tj2
3598  TrajPoint tp2 = tj2.Pts[tj2.EndPt[end2]];
3599  if(tj2.Pts.size() > 50 && tj2.MCSMom > 100) {
3600  if(end1 == 0) {
3601  tp2.Ang = tj2.Pts[tj2.EndPt[0] + 2].Ang;
3602  } else {
3603  tp2.Ang = tj2.Pts[tj2.EndPt[1] - 2].Ang;
3604  }
3605  } else if(loMCSMom) {
3606  // Low momentum - calculate the angle using the two Pts at the end
3607  unsigned short pt1, pt2;
3608  if(end2 == 0) {
3609  pt1 = tj2.EndPt[0];
3610  pt2 = pt1 + 1;
3611  } else {
3612  pt2 = tj2.EndPt[1];
3613  pt1 = pt2 - 1;
3614  }
3615  TrajPoint tpdir;
3616  if(MakeBareTrajPoint(slc, tj2.Pts[pt1], tj2.Pts[pt2], tpdir)) tp2.Ang = tpdir.Ang;
3617  } // low MCSMom
3618 
3619  if(!isVLA && !SignalBetween(slc, tp1, tp2, 0.99)) continue;
3620 
3621  // decide whether to merge or make a vertex
3622  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
3623  float sep = PosSep(tp1.Pos, tp2.Pos);
3624 
3625  float dangCut;
3626  float docaCut;
3627  float chgPull = 0;
3628  if(tp1.AveChg > tp2.AveChg) {
3629  chgPull = (tp1.AveChg / tp2.AveChg - 1) / minChgRMS;
3630  } else {
3631  chgPull = (tp2.AveChg / tp1.AveChg - 1) / minChgRMS;
3632  }
3633  if(loMCSMom) {
3634  // increase dangCut dramatically for low MCSMom tjs
3635  dangCut = 1.0;
3636  // and the doca cut
3637  docaCut = 2;
3638  } else {
3639  // do a more careful calculation of the angle cut
3640  unsigned short e0 = tj1.EndPt[0];
3641  unsigned short e1 = tj1.EndPt[1];
3642  float tj1len = TrajPointSeparation(tj1.Pts[e0], tj1.Pts[e1]);
3643  float thetaRMS1 = MCSThetaRMS(slc, tj1);
3644  // calculate (thetaRMS / sqrt(length) )^2
3645  thetaRMS1 *= thetaRMS1 / tj1len;
3646  // and now tj2
3647  e0 = tj2.EndPt[0];
3648  e1 = tj2.EndPt[1];
3649  float tj2len = TrajPointSeparation(tj2.Pts[e0], tj2.Pts[e1]);
3650  float thetaRMS2 = MCSThetaRMS(slc, tj2);
3651  thetaRMS2 *= thetaRMS2 / tj2len;
3652  float dangErr = 0.5 * sqrt(thetaRMS1 + thetaRMS2);
3653  dangCut = tcc.kinkCuts[0] + tcc.kinkCuts[1] * dangErr;
3654  docaCut = 1;
3655  if(isVLA) docaCut = 15;
3656  }
3657 
3658  // open up the cuts on the last pass
3659  float chgFracCut = tcc.vtx2DCuts[8];
3660  float chgPullCut = tcc.chargeCuts[0];
3661  if(lastPass) {
3662  docaCut *= 2;
3663  chgFracCut *= 0.5;
3664  chgPullCut *= 1.5;
3665  }
3666 
3667  // check the merge cuts. Start with doca and dang requirements
3668  bool doMerge = bestDOCA < docaCut && dang < dangCut;
3669  bool showerTjs = tj1.PDGCode == 11 || tj2.PDGCode == 11;
3670  bool hiMCSMom = tj1.MCSMom > 200 || tj2.MCSMom > 200;
3671  // add a charge similarity requirement if not shower-like or low momentum or not LA
3672  if(doMerge && !showerTjs && hiMCSMom && chgPull > tcc.chargeCuts[0] && !isVLA) doMerge = false;
3673  // ignore the charge pull cut if both are high momentum and dang is really small
3674  if(!doMerge && tj1.MCSMom > 900 && tj2.MCSMom > 900 && dang < 0.1 && bestDOCA < docaCut) doMerge = true;
3675 
3676  // do not merge if chgPull is really high
3677  if(doMerge && chgPull > 2 * chgPullCut) doMerge = false;
3678 
3679  if(doMerge) {
3680  if(lastPass) {
3681  // last pass cuts are looser but ensure that the tj after merging meets the quality cut
3682  float npwc = NumPtsWithCharge(slc, tj1, true) + NumPtsWithCharge(slc, tj2, true);
3683  auto& tp1OtherEnd = tj1.Pts[tj1.EndPt[1 - end1]];
3684  auto& tp2OtherEnd = tj2.Pts[tj2.EndPt[1 - end2]];
3685  float nwires = std::abs(tp1OtherEnd.Pos[0] - tp2OtherEnd.Pos[0]);
3686  if(nwires == 0) nwires = 1;
3687  float hitFrac = npwc / nwires;
3688  doMerge = (hitFrac > tcc.qualityCuts[0]);
3689  // if(prt) mf::LogVerbatim("TC")<<" lastPass merged Tj from "<<PrintPos(slc, tp1OtherEnd.Pos)<<" to "<<PrintPos(slc, tp2OtherEnd.Pos)<<" hitfrac "<<hitFrac<<" cut "<<tcc.qualityCuts[0];
3690  } else {
3691  // don't merge if the gap between them is longer than the length of the shortest Tj
3692  float len1 = TrajLength(slc.tjs[it1]);
3693  float len2 = TrajLength(slc.tjs[it2]);
3694  if(len1 < len2) {
3695  if(sep > len1) doMerge = false;
3696  } else {
3697  if(sep > len2) doMerge = false;
3698  }
3699  // if(prt) mf::LogVerbatim("TC")<<" merge check sep "<<sep<<" len1 "<<len1<<" len2 "<<len2<<" Merge? "<<doMerge;
3700  } // not lastPass
3701  } // doMerge
3702 
3703  // Require a large charge fraction near a merge point
3704  tjlist[0] = slc.tjs[it1].ID;
3705  tjlist[1] = slc.tjs[it2].ID;
3706  float chgFrac = ChgFracNearPos(slc, tp1.Pos, tjlist);
3707  if(doMerge && bestDOCA > 1 && chgFrac < chgFracCut) doMerge = false;
3708 
3709  // don't merge if a Bragg peak exists. A vertex should be made instead
3710  if(doMerge && (tj1.StopFlag[end1][kBragg] || tj2.StopFlag[end2][kBragg])) doMerge = false;
3711 
3712  // Check the MCSMom asymmetry and don't merge if it is higher than the user-specified cut
3713  float momAsym = std::abs(tj1.MCSMom - tj2.MCSMom) / (float)(tj1.MCSMom + tj2.MCSMom);
3714  if(doMerge && momAsym > tcc.vtx2DCuts[9]) doMerge = false;
3715 
3716  if(prt) {
3717  mf::LogVerbatim myprt("TC");
3718  myprt<<"EM: T"<<slc.tjs[it1].ID<<"_"<<end1<<" - T"<<slc.tjs[it2].ID<<"_"<<end2<<" tp1-tp2 "<<PrintPos(slc, tp1)<<"-"<<PrintPos(slc, tp2);
3719  myprt<<" ShowerLike? "<<slc.tjs[it1].AlgMod[kShowerLike]<<" "<<slc.tjs[it2].AlgMod[kShowerLike];
3720  myprt<<" bestFOM "<<std::fixed<<std::setprecision(2)<<bestFOM;
3721  myprt<<" bestDOCA "<<std::setprecision(1)<<bestDOCA;
3722  myprt<<" cut "<<docaCut<<" isVLA? "<<isVLA;
3723  myprt<<" dang "<<std::setprecision(2)<<dang<<" dangCut "<<dangCut;
3724  myprt<<" chgPull "<<std::setprecision(1)<<chgPull<<" Cut "<<chgPullCut;
3725  myprt<<" chgFrac "<<std::setprecision(2)<<chgFrac;
3726  myprt<<" momAsym "<<momAsym;
3727  myprt<<" lastPass? "<<lastPass;
3728  myprt<<" doMerge? "<<doMerge;
3729  }
3730 
3731  if(bestDOCA > docaCut) continue;
3732 
3733  if(doMerge) {
3734  if(prt) mf::LogVerbatim("TC")<<" Merge ";
3735  bool didMerge = false;
3736  if(end1 == 1) {
3737  didMerge = MergeAndStore(slc, it1, it2, tcc.dbgMrg);
3738  } else {
3739  didMerge = MergeAndStore(slc, it2, it1, tcc.dbgMrg);
3740  }
3741  if(didMerge) {
3742  // Set the end merge flag for the killed trajectories to aid tracing merges
3743  tj1.AlgMod[kMerge] = true;
3744  tj2.AlgMod[kMerge] = true;
3745  iterate = true;
3746  } // Merge and store successfull
3747  else {
3748  if(prt) mf::LogVerbatim("TC")<<" MergeAndStore failed ";
3749  }
3750  } else {
3751  // create a vertex instead if it passes the vertex cuts
3752  VtxStore aVtx;
3753  aVtx.CTP = slc.tjs[it1].CTP;
3754  aVtx.ID = slc.vtxs.size() + 1;
3755  // keep it simple if tp1 and tp2 are very close or if the angle between them
3756  // is small
3757  if(PosSep(tp1.Pos, tp2.Pos) < 3 || dang < 0.1) {
3758  aVtx.Pos[0] = 0.5 * (tp1.Pos[0] + tp2.Pos[0]);
3759  aVtx.Pos[1] = 0.5 * (tp1.Pos[1] + tp2.Pos[1]);
3760  aVtx.Stat[kFixed] = true;
3761  } else {
3762  // Tps not so close
3763  // Dec 11, 2017. Require small separation in EndMerge.
3764  // float sepCut = tcc.vtx2DCuts[2];
3765  float sepCut = tcc.vtx2DCuts[1];
3766  bool tj1Short = (slc.tjs[it1].EndPt[1] - slc.tjs[it1].EndPt[0] < maxShortTjLen);
3767  bool tj2Short = (slc.tjs[it2].EndPt[1] - slc.tjs[it2].EndPt[0] < maxShortTjLen);
3768  if(tj1Short || tj2Short) sepCut = tcc.vtx2DCuts[1];
3769  TrajIntersection(tp1, tp2, aVtx.Pos);
3770  float dw = aVtx.Pos[0] - tp1.Pos[0];
3771  if(std::abs(dw) > sepCut) continue;
3772  float dt = aVtx.Pos[1] - tp1.Pos[1];
3773  if(std::abs(dt) > sepCut) continue;
3774  dw = aVtx.Pos[0] - tp2.Pos[0];
3775  if(std::abs(dw) > sepCut) continue;
3776  dt = aVtx.Pos[1] - tp2.Pos[1];
3777  if(std::abs(dt) > sepCut) continue;
3778  // ensure that the vertex is not closer to the other end if the tj is short
3779  if(tj1Short) {
3780  TrajPoint otp1 = slc.tjs[it1].Pts[slc.tjs[it1].EndPt[1-end1]];
3781  if(PosSep2(otp1.Pos, aVtx.Pos) < PosSep2(tp1.Pos, aVtx.Pos)) continue;
3782  }
3783  if(tj2Short) {
3784  TrajPoint otp2 = slc.tjs[it2].Pts[slc.tjs[it2].EndPt[1-end2]];
3785  if(PosSep2(otp2.Pos, aVtx.Pos) < PosSep2(tp2.Pos, aVtx.Pos)) continue;
3786  }
3787  // we expect the vertex to be between tp1 and tp2
3788  if(aVtx.Pos[0] < tp1.Pos[0] && aVtx.Pos[0] < tp2.Pos[0]) {
3789  aVtx.Pos[0] = std::min(tp1.Pos[0], tp2.Pos[0]);
3790  aVtx.Stat[kFixed] = true;
3791  }
3792  if(aVtx.Pos[0] > tp1.Pos[0] && aVtx.Pos[0] > tp2.Pos[0]) {
3793  aVtx.Pos[0] = std::max(tp1.Pos[0], tp2.Pos[0]);
3794  aVtx.Stat[kFixed] = true;
3795  }
3796  } // Tps not so close
3797  // We got this far. Try a vertex fit to ensure that the errors are reasonable
3798  slc.tjs[it1].VtxID[end1] = aVtx.ID;
3799  slc.tjs[it2].VtxID[end2] = aVtx.ID;
3800  // save the position
3801  // do a fit
3802  if(!aVtx.Stat[kFixed] && !FitVertex(slc, aVtx, tcc.dbgMrg)) {
3803  // back out
3804  slc.tjs[it1].VtxID[end1] = 0;
3805  slc.tjs[it2].VtxID[end2] = 0;
3806  if(prt) mf::LogVerbatim("TC")<<" Vertex fit failed ";
3807  continue;
3808  }
3809  aVtx.NTraj = 2;
3810  aVtx.Pass = slc.tjs[it1].Pass;
3811  aVtx.Topo = end1 + end2;
3812  tj1.AlgMod[kMerge] = true;
3813  tj2.AlgMod[kMerge] = true;
3814  // Set pion-like PDGCodes
3815  if(!tj1.Strategy[kStiffEl] && !tj2.Strategy[kStiffEl]) {
3816  if(tj1.StopFlag[end1][kBragg] && !tj2.StopFlag[end2][kBragg]) tj1.PDGCode = 211;
3817  if(tj2.StopFlag[end2][kBragg] && !tj1.StopFlag[end1][kBragg]) tj2.PDGCode = 211;
3818  }
3819  if(!StoreVertex(slc, aVtx)) continue;
3820  SetVx2Score(slc);
3821  if(prt) {
3822  auto& newVx = slc.vtxs[slc.vtxs.size() - 1];
3823  mf::LogVerbatim("TC")<<" New 2V"<<newVx.ID<<" at "<<(int)newVx.Pos[0]<<":"<<(int)(newVx.Pos[1]/tcc.unitsPerTick)<<" Score "<<newVx.Score;
3824  }
3825  // check the score and kill it if it is below the cut
3826  auto& newVx2 = slc.vtxs[slc.vtxs.size() - 1];
3827  if(newVx2.Score < tcc.vtx2DCuts[7] && CompatibleMerge(slc, tj1, tj2, tcc.dbgMrg)) {
3828  slc.tjs[it1].VtxID[end1] = 0;
3829  slc.tjs[it2].VtxID[end2] = 0;
3830  slc.vtxs.pop_back();
3831  bool didMerge = false;
3832  if(end1 == 1) {
3833  didMerge = MergeAndStore(slc, it1, it2, tcc.dbgMrg);
3834  } else {
3835  didMerge = MergeAndStore(slc, it2, it1, tcc.dbgMrg);
3836  }
3837  if(didMerge) {
3838  // Set the end merge flag for the killed trajectories to aid tracing merges
3839  tj1.AlgMod[kMerge] = true;
3840  tj1.AlgMod[kMerge] = true;
3841  iterate = true;
3842  } // Merge and store successfull
3843  else {
3844  if(prt) mf::LogVerbatim("TC")<<" MergeAndStore failed ";
3845  }
3846  }
3847  } // create a vertex
3848  if(tj1.AlgMod[kKilled]) break;
3849  } // end1
3850  } // it1
3851  } // iterate
3852 
3853  ChkVxTjs(slc, inCTP, tcc.dbgMrg);
3854  /*
3855  // Do some checking in debug mode
3856  if(tcc.modes[kDebug] && lastPass) {
3857  for(unsigned short it1 = 0; it1 < slc.tjs.size() - 1; ++it1) {
3858  auto& tj1 = slc.tjs[it1];
3859  if(tj1.CTP != inCTP) continue;
3860  if(tj1.AlgMod[kKilled]) continue;
3861  for(unsigned short end1 = 0; end1 < 2; ++end1) {
3862  unsigned short end2 = 1 - end1;
3863  auto& tp1 = tj1.Pts[tj1.EndPt[end1]];
3864  for(unsigned short it2 = it1 + 1; it2 < slc.tjs.size(); ++it2) {
3865  auto& tj2 = slc.tjs[it2];
3866  if(tj2.CTP != inCTP) continue;
3867  if(tj2.AlgMod[kKilled]) continue;
3868  auto& tp2 = tj2.Pts[tj2.EndPt[end2]];
3869  float sep = PosSep2(tp1.HitPos, tp2.HitPos);
3870  if(sep < 2.5) {
3871  if(tj1.VtxID[end1] == 0 && tj2.VtxID[end2] == 0) {
3872  std::cout<<"Tjs "<<tj1.ID<<" and "<<tj2.ID<<" are close at Pos "<<tj1.CTP<<":"<<PrintPos(slc, tp1.HitPos)<<" "<<tj2.CTP<<":"<<PrintPos(slc, tp2.HitPos)<<" with no merge or vertex\n";
3873  } else if(tj1.VtxID[end1] != tj2.VtxID[end2]) {
3874  std::cout<<"Tjs "<<tj1.ID<<" and "<<tj2.ID<<" are close at Pos "<<tj1.CTP<<":"<<PrintPos(slc, tp1.HitPos);
3875  std::cout<<" but have different vertex IDs "<<tj1.VtxID[end1]<<" != "<<tj2.VtxID[end2];
3876  std::cout<<"\n";
3877  }
3878  } // close points
3879  } // it2
3880  } // end1
3881  } // it1
3882  } // debug mode
3883  */
3884  } // EndMerge
3885 
3887  void MaskTrajEndPoints(TCSlice& slc, Trajectory& tj, unsigned short nPts)
3888  {
3889 
3890  // Masks off (sets all hits not-Used) nPts trajectory points at the leading edge of the
3891  // trajectory, presumably because the fit including this points is poor. The position, direction
3892  // and Delta of the last nPts points is updated as well
3893 
3894  if(tj.Pts.size() < 3) {
3895  mf::LogError("TC")<<"MaskTrajEndPoints: Trajectory ID "<<tj.ID<<" too short to mask hits ";
3896  tj.IsGood = false;
3897  return;
3898  }
3899  if(nPts > tj.Pts.size() - 2) {
3900  mf::LogError("TC")<<"MaskTrajEndPoints: Trying to mask too many points "<<nPts<<" Pts.size "<<tj.Pts.size();
3901  tj.IsGood = false;
3902  return;
3903  }
3904 
3905  // find the last good point (with charge)
3906  unsigned short lastGoodPt = USHRT_MAX ;
3907 
3908  if (!ChkMichel(slc, tj, lastGoodPt)){ //did not find michel electron
3909  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3910  unsigned short ipt = tj.EndPt[1] - nPts - ii;
3911  if(tj.Pts[ipt].Chg > 0) {
3912  lastGoodPt = ipt;
3913  break;
3914  }
3915  if(ipt == 0) break;
3916  } // ii
3917  }
3918  if(tcc.dbgStp) {
3919  mf::LogVerbatim("TC")<<"MTEP: lastGoodPt "<<lastGoodPt<<" Pts size "<<tj.Pts.size()<<" tj.IsGood "<<tj.IsGood;
3920  }
3921  if(lastGoodPt == USHRT_MAX) return;
3922  tj.EndPt[1] = lastGoodPt;
3923 
3924  //for(unsigned short ii = 0; ii < nPts; ++ii) {
3925  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3926  unsigned short ipt = tj.Pts.size() - 1 - ii;
3927  if (ipt==lastGoodPt) break;
3928  UnsetUsedHits(slc, tj.Pts[ipt]);
3929  // Reset the position and direction of the masked off points
3930  tj.Pts[ipt].Dir = tj.Pts[lastGoodPt].Dir;
3931  if(tj.Pts[lastGoodPt].AngleCode == 2) {
3932  // Very large angle: Move by path length
3933  float path = TrajPointSeparation(tj.Pts[lastGoodPt], tj.Pts[ipt]);
3934  tj.Pts[ipt].Pos[0] = tj.Pts[lastGoodPt].Pos[0] + path * tj.Pts[ipt].Dir[0];
3935  tj.Pts[ipt].Pos[1] = tj.Pts[lastGoodPt].Pos[1] + path * tj.Pts[ipt].Dir[1];
3936  } else {
3937  // Not large angle: Move by wire
3938  float dw = tj.Pts[ipt].Pos[0] - tj.Pts[lastGoodPt].Pos[0];
3939  // Correct the projected time to the wire
3940  float newpos = tj.Pts[lastGoodPt].Pos[1] + dw * tj.Pts[ipt].Dir[1] / tj.Pts[ipt].Dir[0];
3941  if(tcc.dbgStp) mf::LogVerbatim("TC")<<"MTEP: ipt "<<ipt<<" Pos[0] "<<tj.Pts[ipt].Pos[0]<<". Move Pos[1] from "<<tj.Pts[ipt].Pos[1]<<" to "<<newpos;
3942  tj.Pts[ipt].Pos[1] = tj.Pts[lastGoodPt].Pos[1] + dw * tj.Pts[ipt].Dir[1] / tj.Pts[ipt].Dir[0];
3943  }
3944  tj.Pts[ipt].Delta = PointTrajDOCA(slc, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tj.Pts[ipt]);
3945  if(tcc.dbgStp) mf::LogVerbatim("TC")<<" masked ipt "<<ipt<<" Pos "<<PrintPos(slc, tj.Pts[ipt])<<" Chg "<<tj.Pts[ipt].Chg;
3946  } // ii
3947  SetEndPoints(tj);
3948 
3949  } // MaskTrajEndPoints
3950 
3952  void ChkStop(TCSlice& slc, Trajectory& tj)
3953  {
3954  // Sets the StopFlag[kBragg] bits on the trajectory by identifying the Bragg peak
3955  // at each end. This function checks both ends, finding the point with the highest charge nearest the
3956  // end and considering the first (when end = 0) 4 points or last 4 points (when end = 1). The next
3957  // 5 - 10 points (fChkStop[0]) are fitted to a line, Q(x - x0) = Qo + (x - x0) * slope where x0 is the
3958  // wire position of the highest charge point. A large negative slope indicates that there is a Bragg
3959  // peak at the end.
3960 
3961  tj.StopFlag[0][kBragg] = false;
3962  tj.StopFlag[1][kBragg] = false;
3963  if(!tcc.useAlg[kChkStop]) return;
3964  if(tcc.chkStopCuts[0] < 0) return;
3965 
3966  // don't attempt with low momentum trajectories
3967  if(tj.MCSMom < 30) return;
3968 
3969  if(tj.Strategy[kStiffEl]) return;
3970 
3971  // ignore trajectories that are very large angle at both ends
3972  if(tj.Pts[tj.EndPt[0]].AngleCode == 2 || tj.Pts[tj.EndPt[1]].AngleCode == 2) return;
3973 
3974  unsigned short nPtsToCheck = tcc.chkStopCuts[1];
3975  if(tj.Pts.size() < 6) return;
3976 
3977  bool prt = (tcc.dbgStp || tcc.dbgAlg[kChkStop]);
3978 
3979  if(prt) {
3980  mf::LogVerbatim("TC")<<"ChkStop: T"<<tj.ID<<" requiring "<<nPtsToCheck<<" points with charge slope > "<<tcc.chkStopCuts[0]<<" Chg/WSEU";
3981  }
3982 
3983  // find the highest charge hit in the first 3 points at each end
3984  for(unsigned short end = 0; end < 2; ++end) {
3985  short dir = 1 - 2 * end;
3986  // find the point with the highest charge considering the first 3 points
3987  float big = 0;
3988  unsigned short hiPt = 0;
3989  float wire0 = 0;
3990  for(unsigned short ii = 0; ii < 5; ++ii) {
3991  short ipt = tj.EndPt[end] + ii * dir;
3992  if(ipt < tj.EndPt[0] || ipt > tj.EndPt[1]) break;
3993  TrajPoint& tp = tj.Pts[ipt];
3994  if(tp.Chg > big) {
3995  big = tp.Chg;
3996  wire0 = tp.Pos[0];
3997  hiPt = ipt;
3998  }
3999  } // ii
4000  if(prt) mf::LogVerbatim("TC")<<" end "<<end<<" wire0 "<<wire0<<" Chg "<<big<<" hiPt "<<hiPt;
4001  float prevChg = big;
4002  // prepare to do the fit
4003  Point2_t inPt;
4004  Vector2_t outVec, outVecErr;
4005  float chgErr, chiDOF;
4006  // Initialize
4007  Fit2D(0, inPt, chgErr, outVec, outVecErr, chiDOF);
4008  unsigned short cnt = 0;
4009  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
4010  short ipt = hiPt + ii * dir;
4011  if(ipt < tj.EndPt[0] || ipt > tj.EndPt[1]) break;
4012  TrajPoint& tp = tj.Pts[ipt];
4013  if(tp.Chg == 0) continue;
4014  // quit if the charge is much larger than the previous charge
4015  if(tp.Chg > 1.5 * prevChg) continue;
4016  prevChg = tp.Chg;
4017  // Accumulate and save points
4018  inPt[0] = std::abs(tp.Pos[0] - wire0);
4019  inPt[1] = tp.Chg;
4020  // Assume 20% point-to-point charge fluctuations
4021  chgErr = 0.2 * tp.Chg;
4022  if(!Fit2D(2, inPt, chgErr, outVec, outVecErr, chiDOF)) break;
4023 // if(tcc.dbgStp) mf::LogVerbatim("TC")<<ipt<<" "<<PrintPos(slc, tp.Pos)<<" "<<inPt[0]<<" Chg "<<(int)tp.Chg;
4024  ++cnt;
4025  if(cnt == nPtsToCheck) break;
4026  } // ii
4027  if(cnt < 4) continue;
4028  // do the fit and get the results
4029  if(!Fit2D(-1, inPt, chgErr, outVec, outVecErr, chiDOF)) continue;
4030  // check for really bad chidof indicating a major failure
4031  if(chiDOF > 500) continue;
4032  // The charge slope is negative for a stopping track in the way that the fit was constructed.
4033  // Flip the sign so we can make a cut against tcc.chkStopCuts[0] which is positive.
4034  outVec[1] = -outVec[1];
4035  if(outVec[1] > tcc.chkStopCuts[0] && chiDOF < tcc.chkStopCuts[2] && outVec[1] > 2 * outVecErr[1]) {
4036  tj.StopFlag[end][kBragg] = true;
4037  tj.AlgMod[kChkStop] = true;
4038  // Put the charge at the end into tp.AveChg
4039  tj.Pts[tj.EndPt[end]].AveChg = outVec[0];
4040  if(prt) mf::LogVerbatim("TC")<<" end "<<end<<" fit chidof "<<chiDOF<<" slope "<<outVec[1]<<" +/- "<<outVecErr[1]<<" stopping";
4041  } else {
4042  if(prt) mf::LogVerbatim("TC")<<" end "<<end<<" fit chidof "<<chiDOF<<" slope "<<outVec[1]<<" +/- "<<outVecErr[1]<<" Not stopping";
4043  }
4044  } // end
4045 
4046  } // ChkStop
4047 
4049  bool ChkMichel(TCSlice& slc, Trajectory& tj, unsigned short& lastGoodPt){
4050 
4051  if(!tcc.useAlg[kMichel]) return false;
4052 
4053  bool prt = (tcc.dbgStp || tcc.dbgAlg[kMichel]);
4054 
4055  //find number of hits that are consistent with Michel electron
4056  unsigned short nmichelhits = 0;
4057  //find number of hits that are consistent with Bragg peak
4058  unsigned short nbragghits = 0;
4059  float lastChg = 0;
4060 
4061  bool isfirsthit = true;
4062  unsigned short braggpeak = 0;
4063 
4064  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
4065  if (ii>tj.EndPt[1]) continue;
4066  unsigned short ipt = tj.EndPt[1] - ii;
4067  if (tj.Pts[ipt].Chg>0){
4068  if (isfirsthit){
4069  isfirsthit = false;
4070  if (tj.Pts[ipt].ChgPull<0){
4071  ++nmichelhits;
4072  }
4073  }
4074  else{
4075  if (tj.Pts[ipt].ChgPull<0&&nmichelhits&&!nbragghits){//still Michel
4076  ++nmichelhits;
4077  }
4078  else{
4079  if (!nbragghits){
4080  ++nbragghits; //Last Bragg peak hit
4081  lastChg = tj.Pts[ipt].Chg;
4082  braggpeak = ipt;
4083  }
4084  else if (tj.Pts[ipt].Chg<lastChg){ //still Bragg peak
4085  ++nbragghits;
4086  lastChg = tj.Pts[ipt].Chg;
4087  }
4088  else break;
4089  }
4090  }
4091  }
4092  }
4093  if(prt) mf::LogVerbatim("TC")<<"ChkMichel Michel hits: "<<nmichelhits<<" Bragg peak hits: "<<nbragghits;
4094  if (nmichelhits>0&&nbragghits>2){//find Michel topology
4095  lastGoodPt = braggpeak;
4096  tj.AlgMod[kMichel] = true;
4097  return true;
4098  }
4099  else{
4100  return false;
4101  }
4102  }
4103 
4105  void ChkHiChgHits(TCSlice& slc, CTP_t inCTP)
4106  {
4107  // Check allTraj trajectories in the current CTP to see if they are stopping
4108  if(!tcc.useAlg[kSplitHiChgHits]) return;
4109 
4110  for(size_t i = 0; i< slc.tjs.size(); ++i) {
4111  auto & tj = slc.tjs[i];
4112  if(tj.CTP != inCTP) continue;
4113  if(tj.AlgMod[kKilled]) continue;
4114  SplitHiChgHits(slc, tj);
4115  } // tj
4116 
4117  } // ChkHiChgHits
4118 
4121 
4122  // Check allTraj trajectories in the current CTP and split high charge hits
4123  if(!tcc.useAlg[kSplitHiChgHits]) return;
4124  // Only do it once
4125  if (tj.AlgMod[kSplitHiChgHits]) return;
4126  if(tj.AlgMod[kKilled]) return;
4127  //Ignore short trajectories
4128  if (tj.EndPt[1]<10) return;
4129 
4130  bool prt = (tcc.dbgStp || tcc.dbgAlg[kSplitHiChgHits]);
4131 
4132  for(unsigned short end = 0; end < 2; ++end) {
4133  if(prt) mf::LogVerbatim("TC")<<"SplitHiChghits "<<end<<" "<<tj.VtxID[end];
4134  float hichg = 0;
4135  unsigned short tp = tj.EndPt[end];
4136  unsigned short nlohits = 0;
4137  unsigned short lastHiTP = USHRT_MAX;
4138  while (tp != tj.EndPt[1-end]){
4139  float ptchg = TpSumHitChg(slc, tj.Pts[tp]);
4140  if (prt) mf::LogVerbatim("TC")<<"SplitHiChgHits "<<tp<<" "<<ptchg<<" "<<PrintPos(slc, tj.Pts[tp]);
4141  if (ptchg){
4142  if (tp == tj.EndPt[end]){
4143  hichg = ptchg;
4144  lastHiTP = tp;
4145  }
4146  else if (ptchg>0.4*hichg){
4147  if (!nlohits){
4148  hichg = ptchg;
4149  lastHiTP = tp;
4150  }
4151  else{
4152  break;
4153  }
4154  }
4155  else ++nlohits;
4156  }
4157  if (end==0){
4158  ++tp;
4159  }
4160  else{
4161  --tp;
4162  }
4163  }
4164  //if (tcc.dbgStp) mf::LogVerbatim("TC")<<"SplitHiChgHits "<<end<<" "<<nlohits;
4165  if (nlohits>4&&lastHiTP!=USHRT_MAX){
4166  //Create new vertex
4167  VtxStore aVtx;
4168  aVtx.Pos = tj.Pts[lastHiTP].Pos;
4169  aVtx.NTraj = 2;
4170  aVtx.Pass = tj.Pass;
4171  aVtx.Topo = 7;
4172  aVtx.ChiDOF = 0;
4173  aVtx.CTP = tj.CTP;
4174  aVtx.ID = slc.vtxs.size() + 1;
4175  if(!StoreVertex(slc, aVtx)) {
4176  if(prt) mf::LogVerbatim("TC")<<" Failed storing vertex "<<tj.VtxID[end];
4177  return;
4178  }
4179 
4180  // make a copy
4181  Trajectory newTj = tj;
4182  newTj.ID = slc.tjs.size() + 1;
4183 
4184  // keep high charge hits, reassign other hits to the new trajectory
4185  unsigned short tp1 = lastHiTP+1;
4186  if (end==1) tp1 = lastHiTP-1;
4187  for (unsigned short ipt = std::min(tj.EndPt[1-end], tp1); ipt <= std::max(tj.EndPt[1-end], tp1); ++ipt){
4188  tj.Pts[ipt].Chg = 0;
4189  for (unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
4190  if(!tj.Pts[ipt].UseHit[ii]) continue;
4191  unsigned int iht = tj.Pts[ipt].Hits[ii];
4192  // This shouldn't happen but check anyway
4193  if(slc.slHits[iht].InTraj != tj.ID) continue;
4194  slc.slHits[iht].InTraj = newTj.ID;
4195  tj.Pts[ipt].UseHit[ii] = false;
4196  }//ii
4197  }//ipt
4198  SetEndPoints(tj);
4199  tj.VtxID[1-end] = aVtx.ID;
4200  tj.AlgMod[kSplitHiChgHits] = true;
4201  if(prt) {
4202  mf::LogVerbatim("TC")<<"Splitting trajectory ID "<<tj.ID<<" new EndPts "<<tj.EndPt[0]<<" to "<<tj.EndPt[1];
4203  }
4204 
4205  for (unsigned short ipt = std::min(newTj.EndPt[end], lastHiTP); ipt <= std::max(newTj.EndPt[end], lastHiTP); ++ipt){
4206  newTj.Pts[ipt].Chg = 0;
4207  for (unsigned short ii = 0; ii < newTj.Pts[ipt].Hits.size(); ++ii) {
4208  newTj.Pts[ipt].UseHit[ii] = false;
4209  }//ii
4210  }//ipt
4211  SetEndPoints(newTj);
4212  newTj.VtxID[end] = aVtx.ID;
4213  newTj.AlgMod[kSplitHiChgHits] = true;
4214  slc.tjs.push_back(newTj);
4215  SetVx2Score(slc);
4216 
4217  break;
4218  }
4219  }
4220  }
4221 
4223  bool MakeJunkTraj(TCSlice& slc, std::vector<unsigned int> tHits)
4224  {
4225  if(!tcc.useAlg[kJunkTj]) return false;
4226  // Make a crummy trajectory using the provided hits
4227 
4228  if(tHits.size() < 2) return false;
4229 
4230  bool prt = (tcc.dbgStp || tcc.dbgAlg[kJunkTj]);
4231 
4232  // Start the trajectory using the first and last hits to
4233  // define a starting direction. Use the last pass settings
4234  Trajectory work;
4235  unsigned short pass = tcc.minPts.size() - 1;
4236  if(!StartTraj(slc, work, tHits[0], tHits[tHits.size()-1], pass)) return false;
4237  // make a TP for every hit
4238  work.Pts.resize(tHits.size());
4239  // fit all of the hits to a line
4240  Point2_t inPt;
4241  Vector2_t outVec, outVecErr;
4242  float inPtErr = 1, chiDOF;
4243  // initialize
4244  Fit2D(0, inPt, inPtErr, outVec, outVecErr, chiDOF);
4245  std::vector<Point2_t> fitPts(tHits.size());
4246  for(unsigned short ii = 0; ii < tHits.size(); ++ii) {
4247  unsigned int iht = tHits[ii];
4248  if(slc.slHits[iht].InTraj == SHRT_MAX) return false;
4249  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4250  inPt[0] = hit.WireID().Wire;
4251  inPt[1] = hit.PeakTime() * tcc.unitsPerTick;
4252  fitPts[ii] = inPt;
4253  // accumulate. Don't store the points since we don't care about chisq
4254  Fit2D(1, inPt, inPtErr, outVec, outVecErr, chiDOF);
4255  } // ii
4256  if(!Fit2D(-1, inPt, inPtErr, outVec, outVecErr, chiDOF)) return false;
4257 
4258  if(prt) mf::LogVerbatim("TC")<<" tHits line fit Angle "<<atan(outVec[1]);
4259  // A rough estimate of the trajectory angle
4260  work.Pts[0].Ang = atan(outVec[1]);
4261  work.Pts[0].Dir[0] = cos(work.Pts[0].Ang);
4262  work.Pts[0].Dir[1] = sin(work.Pts[0].Ang);
4263  SetAngleCode(work.Pts[0]);
4264  // clone this information for all of the other points
4265  for(unsigned short ipt = 1; ipt < work.Pts.size(); ++ipt) {
4266  auto& tp = work.Pts[ipt];
4267  tp.CTP = work.CTP;
4268  tp.Ang = work.Pts[0].Ang;
4269  tp.Dir = work.Pts[0].Dir;
4270  tp.AngleCode = work.Pts[0].AngleCode;
4271  } // ipt
4272  // Rotate the hits into this coordinate system to find the start and end
4273  // points and general direction
4274  double cs = cos(-work.Pts[0].Ang);
4275  double sn = sin(-work.Pts[0].Ang);
4276  float tAlong, minAlong = 1E6, maxAlong = -1E6;
4277  // sort the hits by the distance along the general direction
4278  std::vector<SortEntry> sortVec(tHits.size());
4279  SortEntry sortEntry;
4280  for(unsigned short ii = 0; ii < fitPts.size(); ++ii) {
4281  tAlong = cs * fitPts[ii][0] - sn * fitPts[ii][1];
4282  if(tAlong < minAlong) minAlong = tAlong;
4283  if(tAlong > maxAlong) maxAlong = tAlong;
4284  sortEntry.index = ii;
4285  sortEntry.val = tAlong;
4286  sortVec[ii] = sortEntry;
4287  } // ii
4288  std::sort(sortVec.begin(), sortVec.end(), valsDecreasing);
4289  // put a hit into each TP
4290  for(unsigned short ipt = 0; ipt < work.Pts.size(); ++ipt) {
4291  auto& tp = work.Pts[ipt];
4292  tp.Hits.resize(1);
4293  tp.Hits[0] = tHits[sortVec[ipt].index];
4294  tp.UseHit[0] = true;
4295  DefineHitPos(slc, tp);
4296  // Just use the hit position as the tj position
4297  tp.Pos = tp.HitPos;
4298  } // ipt
4299  SetEndPoints(work);
4300  work.AlgMod[kJunkTj] = true;
4301  work.IsGood = true;
4302  if(prt) {
4303  PrintTrajectory("MJT", slc, work, USHRT_MAX);
4304  }
4305  // Finally push it onto slc.tjs
4306  if(!StoreTraj(slc, work)) {
4307  ReleaseHits(slc, work);
4308  return false;
4309  }
4310  return true;
4311  } // MakeJunkTraj
4312 /*
4314  bool MakeJunkTraj(TCSlice& slc, std::vector<unsigned int> tHits)
4315  {
4316 
4317  if(!tcc.useAlg[kJunkTj]) return false;
4318  // Make a crummy trajectory using the provided hits
4319 
4320  if(tHits.size() < 2) return false;
4321 
4322  bool prt = (tcc.dbgStp || tcc.dbgAlg[kJunkTj]);
4323  std::vector<std::vector<unsigned int>> tpHits;
4324 
4325  // Start the trajectory using the first and last hits to
4326  // define a starting direction. Use the last pass settings
4327  Trajectory work;
4328  unsigned short pass = tcc.minPts.size() - 1;
4329  if(!StartTraj(slc, work, tHits[0], tHits[tHits.size()-1], pass)) return false;
4330 
4331  // Make TPs with the same separation as the wire spacing
4332  constexpr float pointSize = 1;
4333 
4334  // Do a more detailed specification of TPs if there
4335  // are enough hits
4336  if(tHits.size() > 6) {
4337  // fit all of the hits to a line
4338  Point2_t inPt;
4339  Vector2_t outVec, outVecErr;
4340  float inPtErr = 1, chiDOF;
4341  // initialize
4342  Fit2D(0, inPt, inPtErr, outVec, outVecErr, chiDOF);
4343  std::vector<Point2_t> fitPts(tHits.size());
4344  for(unsigned short ii = 0; ii < tHits.size(); ++ii) {
4345  unsigned int iht = tHits[ii];
4346  if(slc.slHits[iht].InTraj == SHRT_MAX) return false;
4347  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4348  inPt[0] = hit.WireID().Wire;
4349  inPt[1] = hit.PeakTime() * tcc.unitsPerTick;
4350  fitPts[ii] = inPt;
4351  // accumulate. Don't store the points since we don't care about chisq
4352  Fit2D(1, inPt, inPtErr, outVec, outVecErr, chiDOF);
4353  } // ii
4354  if(!Fit2D(-1, inPt, inPtErr, outVec, outVecErr, chiDOF)) return false;
4355 
4356  if(prt) mf::LogVerbatim("TC")<<" tHits line fit Angle "<<atan(outVec[1]);
4357  // A rough estimate of the trajectory angle
4358  work.Pts[0].Ang = atan(outVec[1]);
4359  work.Pts[0].Dir[0] = cos(work.Pts[0].Ang);
4360  work.Pts[0].Dir[1] = sin(work.Pts[0].Ang);
4361  SetAngleCode(work.Pts[0]);
4362  // Rotate the hits into this coordinate system to find the start and end
4363  // points and general direction
4364  double cs = cos(-work.Pts[0].Ang);
4365  double sn = sin(-work.Pts[0].Ang);
4366  float tAlong, minAlong = 1E6, maxAlong = -1E6;
4367  // sort the hits by the distance along the general direction
4368  std::vector<SortEntry> sortVec(tHits.size());
4369  SortEntry sortEntry;
4370  for(unsigned short ii = 0; ii < fitPts.size(); ++ii) {
4371  tAlong = cs * fitPts[ii][0] - sn * fitPts[ii][1];
4372  if(tAlong < minAlong) minAlong = tAlong;
4373  if(tAlong > maxAlong) maxAlong = tAlong;
4374  sortEntry.index = ii;
4375  sortEntry.val = tAlong;
4376  sortVec[ii] = sortEntry;
4377  } // ii
4378  std::sort(sortVec.begin(), sortVec.end(), valsDecreasing);
4379  // make a temp vector
4380  std::vector<unsigned int> tmp(sortVec.size());
4381  // overwrite with the sorted values
4382  for(unsigned short ii = 0; ii < sortVec.size(); ++ii) tmp[ii] = tHits[sortVec[ii].index];
4383  tHits = tmp;
4384  // create a trajectory point at each WSE unit (if there are hits at that point)
4385  unsigned short npts = (unsigned short)((maxAlong - minAlong) / pointSize);
4386  // rotate back into normal coordinate system
4387  if(prt) mf::LogVerbatim("TC")<<" minAlong "<<minAlong<<" maxAlong "<<maxAlong<<" work.Pts[0].Ang "<<work.Pts[0].Ang<<" npts "<<npts;
4388  if(npts < 2) npts = 2;
4389  tpHits.resize(npts);
4390  for(unsigned short ii = 0; ii < tHits.size(); ++ii) {
4391  unsigned short ipt = ((sortVec[ii].val - minAlong) / pointSize);
4392  if(ipt > npts - 1) ipt = npts - 1;
4393  if(prt) mf::LogVerbatim("TC")<<"tHit "<<PrintHit(slc.slHits[tHits[ii]])<<" length "<<sortVec[ii].val<<" ipt "<<ipt;
4394  if(tpHits[ipt].size() < 16) tpHits[ipt].push_back(tHits[ii]);
4395  }
4396  } else {
4397  // just a few hits. Put each one at a TP in the order that
4398  // they were found
4399  tpHits.resize(tHits.size());
4400  for(unsigned short ii = 0; ii < tHits.size(); ++ii) tpHits[ii].push_back(tHits[ii]);
4401  } // tHits.size()
4402  // make the TPs
4403  // work.Pts[0] is already defined but it needs hits added
4404  work.Pts[0].Hits = tpHits[0];
4405  for(auto iht : tpHits[0]) slc.slHits[iht].InTraj = work.ID;
4406  // set all bits true
4407  work.Pts[0].UseHit.set();
4408  DefineHitPos(slc, work.Pts[0]);
4409  work.Pts[0].Pos = work.Pts[0].HitPos;
4410  if(prt) PrintTrajectory("MJT", slc, work, USHRT_MAX);
4411  // another TP to get the direction
4412  TrajPoint tpd;
4413  // make the rest of the TPs
4414  for(unsigned short ipt = 1; ipt < tpHits.size(); ++ipt) {
4415  if(tpHits[ipt].empty()) continue;
4416  // Use the previous TP as a starting point
4417  unsigned short lastPt = work.Pts.size() - 1;
4418  TrajPoint tp = work.Pts[lastPt];
4419  tp.Step = ipt;
4420  tp.Hits = tpHits[ipt];
4421  for(auto iht : tpHits[ipt]) slc.slHits[iht].InTraj = work.ID;
4422  // use all hits
4423  tp.UseHit.set();
4424  DefineHitPos(slc, tp);
4425  // Just use the hit position as the tj position
4426  tp.Pos = tp.HitPos;
4427  if(TrajPointSeparation(work.Pts[ipt-1], tp) < 0.5) {
4428  for(auto iht : tpHits[ipt]) slc.slHits[iht].InTraj = 0;
4429  continue;
4430  }
4431  work.Pts.push_back(tp);
4432  SetEndPoints(work);
4433  }
4434  if(prt) {
4435  PrintTrajectory("MJT", slc, work, USHRT_MAX);
4436  }
4437  work.AlgMod[kJunkTj] = true;
4438  work.IsGood = true;
4439  // Finally push it onto slc.tjs
4440  slc.isValid = StoreTraj(slc, work);
4441  if(!slc.isValid) {
4442  ReleaseHits(slc, work);
4443  return false;
4444  }
4445  if(tcc.useAlg[kChkInTraj]) {
4446  slc.isValid = InTrajOK(slc, "MJT");
4447  if(!slc.isValid) {
4448  ReleaseHits(slc, work);
4449  mf::LogVerbatim("TC")<<"InTrajOK failed in MakeJunkTraj";
4450  return false;
4451  }
4452  }
4453  return true;
4454  } // MakeJunkTraj
4455 */
4456 
4457 } // namespace tca
void PrintTrajectory(std::string someText, TCSlice &slc, const Trajectory &tj, unsigned short tPoint)
Definition: Utils.cxx:5455
std::bitset< 16 > UseHit
Definition: DataStructs.h:137
float HitsRMSTime(TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:3700
Vector2_t Dir
Definition: DataStructs.h:122
float AveChg
Calculated using ALL hits.
Definition: DataStructs.h:148
void ReleaseHits(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1150
float HitsRMSTick(TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:3706
Point2_t Pos
Definition: DataStructs.h:121
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< Trajectory > tjs
vector of all trajectories in each plane
Definition: DataStructs.h:570
void StepAway(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:15
bool MaskedHitsOK(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2710
void SetEndPoints(Trajectory &tj)
Definition: Utils.cxx:2864
void UpdateTraj(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:655
bool MakeBareTrajPoint(TCSlice &slc, unsigned int fromHit, unsigned int toHit, TrajPoint &tp)
Definition: Utils.cxx:3581
void FixTrajEnd(TCSlice &slc, Trajectory &tj, unsigned short atPt)
Definition: StepUtils.cxx:3173
std::vector< float > kinkCuts
kink angle, nPts fit, (alternate) kink angle significance
Definition: DataStructs.h:475
void FindSoftKink(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2211
bool valsDecreasing(SortEntry c1, SortEntry c2)
Definition: StepUtils.cxx:9
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:54
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:119
std::vector< float > maxPos0
Definition: DataStructs.h:489
unsigned short NTPsFit
Definition: DataStructs.h:133
std::vector< unsigned short > minPtsFit
Reconstruct in several passes.
Definition: DataStructs.h:482
bool FitVertex(TCSlice &slc, VtxStore &vx, bool prt)
Definition: TCVertex.cxx:2131
short MCSMom(TCSlice &slc, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2914
std::vector< float > qualityCuts
Min points/wire, min consecutive pts after a gap.
Definition: DataStructs.h:479
unsigned int index
Definition: StepUtils.cxx:5
void AddHits(TCSlice &slc, Trajectory &tj, unsigned short ipt, bool &sigOK)
Definition: StepUtils.cxx:1091
TCConfig tcc
Definition: DataStructs.cxx:6
unsigned short Step
Definition: DataStructs.h:134
std::vector< unsigned int > PutTrajHitsInVector(Trajectory const &tj, HitStatus_t hitRequest)
Definition: Utils.cxx:2416
vertex position fixed manually - no fitting done
Definition: DataStructs.h:74
std::array< std::bitset< 8 >, 2 > StopFlag
Definition: DataStructs.h:165
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
void UnsetUsedHits(TCSlice &slc, TrajPoint &tp)
Definition: Utils.cxx:1162
std::vector< unsigned int > Hits
Definition: DataStructs.h:136
step from US -> DS (true) or DS -> US (false)
Definition: DataStructs.h:447
void SetStrategy(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:303
bool StoreTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1175
float TotChg
Total including an estimate for dead wires.
Definition: DataStructs.h:149
void FillGaps(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2264
unsigned short Pass
Definition: DataStructs.h:58
float maxWireSkipWithSignal
max number of wires to skip with a signal on them
Definition: DataStructs.h:500
bool IsGhost(TCSlice &slc, std::vector< unsigned int > &tHits)
Definition: StepUtils.cxx:3385
void FindUseHits(TCSlice &slc, Trajectory &tj, unsigned short ipt, float maxDelta, bool useChg)
Definition: StepUtils.cxx:1923
void ChkChgAsymmetry(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1611
float ExpectedHitsRMS(TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:1802
float TrajPointSeparation(TrajPoint &tp1, TrajPoint &tp2)
Definition: Utils.cxx:2368
void SetAngleCode(TrajPoint &tp)
Definition: Utils.cxx:722
Float_t tmp
Definition: plot.C:37
bool MakeJunkTraj(TCSlice &slc, std::vector< unsigned int > tHits)
Definition: StepUtils.cxx:4223
double DeltaAngle(const Vector3_t v1, const Vector3_t v2)
Definition: PFPUtils.cxx:1636
bool dbgSlc
debug only in the user-defined slice? default is all slices
Definition: DataStructs.h:507
bool StartTraj(TCSlice &slc, Trajectory &tj, unsigned int fromhit, unsigned int tohit, unsigned short pass)
Definition: Utils.cxx:4431
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
std::vector< unsigned int > lastWire
the last wire with a hit
Definition: DataStructs.h:555
bool LongPulseHit(const recob::Hit &hit)
Definition: Utils.cxx:3936
bool IsGood
set false if there is a failure or the Tj fails quality cuts
Definition: DataStructs.h:168
float DeadWireCount(TCSlice &slc, const TrajPoint &tp1, const TrajPoint &tp2)
Definition: Utils.cxx:1881
bool StoreVertex(TCSlice &slc, VtxStore &vx)
Definition: TCVertex.cxx:2093
bool doForecast
See TCMode_t above.
Definition: DataStructs.h:525
bool TrajTrajDOCA(TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2, unsigned short &ipt1, unsigned short &ipt2, float &minSep)
Definition: Utils.cxx:2175
unsigned short Pass
the pass on which it was created
Definition: DataStructs.h:161
void HiEndDelta(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2566
int TDCtick_t
Type representing a TDC tick.
Definition: RawTypes.h:24
std::vector< std::vector< std::pair< int, int > > > wireHitRange
Definition: DataStructs.h:575
std::vector< unsigned short > maxAngleCode
max allowed angle code for each pass
Definition: DataStructs.h:484
float projectionErrFactor
Definition: DataStructs.h:501
const std::vector< std::string > StrategyBitNames
std::vector< unsigned int > FindCloseHits(TCSlice &slc, std::array< int, 2 > const &wireWindow, Point2_t const &timeWindow, const unsigned short plane, HitStatus_t hitRequest, bool usePeakTime, bool &hitsNear)
Definition: Utils.cxx:2496
Int_t max
Definition: plot.C:27
TCanvas * c1
Definition: plotHisto.C:7
bool dbgStp
debug stepping using debug.Cryostat, debug.TPC, etc
Definition: DataStructs.h:508
TCanvas * c2
Definition: plot_hist.C:75
std::array< float, 2 > Point2_t
Definition: DataStructs.h:37
std::vector< float > maxPos1
Definition: DataStructs.h:490
float unitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:488
use the slowing-down strategy
Definition: DataStructs.h:420
bool TrajIsClean(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:2891
std::vector< short > minMCSMom
Min MCSMom for each pass.
Definition: DataStructs.h:485
void DefineHitPos(TCSlice &slc, TrajPoint &tp)
Definition: StepUtils.cxx:1840
DebugStuff debug
Definition: DebugStruct.cxx:4
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:144
bool SignalBetween(TCSlice &slc, const TrajPoint &tp1, const TrajPoint &tp2, const float &MinWireSignalFraction)
Definition: Utils.cxx:1671
void SplitHiChgHits(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:4120
void CheckStiffEl(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:898
bool dbg2V
debug 2D vertex finding
Definition: DataStructs.h:510
std::vector< float > aveHitRMS
average RMS of an isolated hit
Definition: DataStructs.h:541
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:143
float TrajLength(Trajectory &tj)
Definition: Utils.cxx:2337
void FixTrajBegin(TCSlice &slc, Trajectory &tj, unsigned short atPt)
Definition: StepUtils.cxx:3092
std::array< unsigned short, 2 > EndPt
First and last point in the trajectory that has charge.
Definition: DataStructs.h:156
unsigned short PDGCode
shower-like or track-like {default is track-like}
Definition: DataStructs.h:160
bool SignalAtTp(TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:1814
void TagJunkTj(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:2442
void ChkHiChgHits(TCSlice &slc, CTP_t inCTP)
Definition: StepUtils.cxx:4105
short StartEnd
The starting end (-1 = don&#39;t know)
Definition: DataStructs.h:163
double PosSep2(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1660
void FitTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:753
std::string PrintHit(const TCHit &tch)
Definition: Utils.cxx:5704
void SetPDGCode(TCSlice &slc, unsigned short itj, bool tjDone)
Definition: Utils.cxx:3795
Point2_t Pos
Definition: DataStructs.h:55
bool Fit2D(short mode, Point2_t inPt, float &inPtErr, Vector2_t &outVec, Vector2_t &outVecErr, float &chiDOF)
Definition: Utils.cxx:4536
std::vector< unsigned short > minPts
min number of Pts required to make a trajectory
Definition: DataStructs.h:483
float TpSumHitChg(TCSlice &slc, TrajPoint const &tp)
Definition: Utils.cxx:1853
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:258
void GottaKink(TCSlice &slc, Trajectory &tj, unsigned short &killPts)
Definition: StepUtils.cxx:2842
std::array< double, 2 > Vector2_t
Definition: DataStructs.h:38
void ReverseTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:2751
unsigned short NumHitsInTP(const TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3775
void TrimEndPts(std::string fcnLabel, TCSlice &slc, Trajectory &tj, const std::vector< float > &fQualityCuts, bool prt)
Definition: Utils.cxx:1511
std::vector< unsigned int > firstWire
the first wire with a hit
Definition: DataStructs.h:554
int ID
ID that is local to one slice.
Definition: DataStructs.h:157
std::vector< TCSlice > slices
Definition: DataStructs.cxx:10
std::array< unsigned short, 2 > VtxID
ID of 2D vertex.
Definition: DataStructs.h:155
Detector simulation of raw signals on wires.
void UpdateStiffEl(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:633
void UpdateTjChgProperties(std::string inFcnLabel, TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:3196
void ChkStop(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:3952
unsigned short NumPtsWithCharge(TCSlice &slc, const Trajectory &tj, bool includeDeadWires)
Definition: Utils.cxx:1863
std::bitset< 16 > modes
number of points to find AveChg
Definition: DataStructs.h:524
float HitTimeErr(TCSlice &slc, unsigned int iht)
Definition: StepUtils.cxx:1693
std::vector< TCHit > slHits
Definition: DataStructs.h:569
std::vector< float > chargeCuts
Definition: DataStructs.h:478
bool StopIfBadFits(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2815
bool MergeAndStore(TCSlice &slc, unsigned int itj1, unsigned int itj2, bool doPrt)
Definition: Utils.cxx:4095
void MaskBadTPs(TCSlice &slc, Trajectory &tj, float const &maxChi)
Definition: StepUtils.cxx:2665
std::bitset< 16 > Stat
Vertex status bits using kVtxBit_t.
Definition: DataStructs.h:69
int ID
set to 0 if killed
Definition: DataStructs.h:64
std::vector< TrajPoint > seeds
Definition: DataStructs.cxx:11
float ChgFracNearPos(TCSlice &slc, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2698
unsigned short AngleCode
Definition: DataStructs.h:135
float maxWireSkipNoSignal
max number of wires to skip w/o a signal on them
Definition: DataStructs.h:499
void TrajIntersection(TrajPoint const &tp1, TrajPoint const &tp2, Point2_t &pos)
Definition: Utils.cxx:2299
float val
Definition: StepUtils.cxx:6
void CheckTraj(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:911
unsigned int CTP_t
Definition: DataStructs.h:41
std::vector< TjForecast > tjfs
Definition: DataStructs.cxx:7
float MCSThetaRMS(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:2984
void AddLAHits(TCSlice &slc, Trajectory &tj, unsigned short ipt, bool &sigOK)
Definition: StepUtils.cxx:1277
bool AttachTrajToVertex(TCSlice &slc, Trajectory &tj, VtxStore &vx, bool prt)
Definition: TCVertex.cxx:1893
std::bitset< 128 > useAlg
Max hit separation for making junk trajectories. < 0 to turn off.
Definition: DataStructs.h:504
float HitsTimeErr2(TCSlice &slc, const std::vector< unsigned int > &hitVec)
Definition: StepUtils.cxx:1701
TDirectory * dir
Definition: macro.C:5
bool HasDuplicateHits(TCSlice &slc, Trajectory const &tj, bool prt)
Definition: Utils.cxx:2469
void CheckHiMultUnusedHits(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2378
Int_t min
Definition: plot.C:26
void ChgSlope(TCSlice &slc, Trajectory &tj, float &slope, float &slopeErr, float &chiDOF)
Definition: Utils.cxx:1288
float VLAStepSize
Definition: DataStructs.h:502
std::vector< VtxStore > vtxs
2D vertices
Definition: DataStructs.h:576
short StepDir
-1 = going US (-> small wire#), 1 = going DS (-> large wire#)
Definition: DataStructs.h:162
float MaxHitDelta(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:2734
std::bitset< 128 > AlgMod
Bit set if algorithm AlgBit_t modifed the trajectory.
Definition: DataStructs.h:145
std::vector< short > muonTag
min length and min MCSMom for a muon tag
Definition: DataStructs.h:471
void Forecast(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:404
unsigned short NTraj
Definition: DataStructs.h:57
geo::PlaneID DecodeCTP(CTP_t CTP)
double PosSep(const Point3_t &pos1, const Point3_t &pos2)
Definition: PFPUtils.cxx:1654
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
std::bitset< 128 > dbgAlg
Allow user to turn on debug printing in algorithms (that print...)
Definition: DataStructs.h:505
std::vector< recob::Hit > const * allHits
Definition: DataStructs.h:536
std::string PrintPos(TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:5712
float OverlapFraction(TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2)
Definition: Utils.cxx:664
float TPHitsRMSTick(TCSlice &slc, TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3674
std::vector< unsigned int > nWires
Definition: DataStructs.h:553
float PointTrajDOCA(TCSlice &slc, unsigned int iht, TrajPoint const &tp)
Definition: Utils.cxx:2270
use the stiff electron strategy
Definition: DataStructs.h:418
std::vector< float > chkStopCuts
[Min Chg ratio, Chg slope pull cut, Chg fit chi cut]
Definition: DataStructs.h:473
void EndMerge(TCSlice &slc, CTP_t inCTP, bool lastPass)
Definition: StepUtils.cxx:3464
unsigned short TPNearVertex(TCSlice &slc, const TrajPoint &tp)
Definition: TCVertex.cxx:1816
void GetHitMultiplet(TCSlice &slc, unsigned int theHit, std::vector< unsigned int > &hitsInMultiplet, unsigned short &localIndex)
Definition: StepUtils.cxx:1580
void SetVx2Score(TCSlice &slc)
Definition: TCVertex.cxx:2504
std::string PrintStopFlag(const Trajectory &tj, unsigned short end)
Definition: Utils.cxx:5677
std::vector< float > vtx2DCuts
Max position pull, max Position error rms.
Definition: DataStructs.h:466
std::bitset< 8 > Strategy
Definition: DataStructs.h:166
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
float multHitSep
preferentially "merge" hits with < this separation
Definition: DataStructs.h:491
kink found in CheckTraj
Definition: DataStructs.h:351
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2485
TCEvent evt
Definition: DataStructs.cxx:5
void MaskTrajEndPoints(TCSlice &slc, Trajectory &tj, unsigned short nPts)
Definition: StepUtils.cxx:3887
void UpdateDeltaRMS(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2635
void ChkStopEndPts(TCSlice &slc, Trajectory &tj, bool prt)
Definition: StepUtils.cxx:1711
void ReversePropagate(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:1414
bool NeedsUpdate
Set true when the Tj needs to be updated.
Definition: DataStructs.h:167
void CheckHiMultEndHits(TCSlice &slc, Trajectory &tj)
Definition: StepUtils.cxx:2526
Point2_t HitPos
Definition: DataStructs.h:120
void PrintTrajPoint(std::string someText, TCSlice &slc, unsigned short ipt, short dir, unsigned short pass, TrajPoint const &tp)
Definition: Utils.cxx:5541
void ChkVxTjs(TCSlice &slc, const CTP_t &inCTP, bool prt)
Definition: TCVertex.cxx:874
CTP_t CTP
set to an invalid CTP
Definition: DebugStruct.h:22
double HitPosErr2
Definition: DataStructs.h:123
unsigned short AngleRange(TrajPoint const &tp)
Definition: Utils.cxx:716
use the stiff muon strategy
Definition: DataStructs.h:419
bool CompatibleMerge(TCSlice &slc, std::vector< int > &tjIDs, bool prt)
Definition: Utils.cxx:534
bool ChkMichel(TCSlice &slc, Trajectory &tj, unsigned short &lastGoodPt)
Definition: StepUtils.cxx:4049