LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
Utils.cxx
Go to the documentation of this file.
2 
3 #include "larcorealg/CoreUtils/NumericUtils.h" // util::absDiff()
4 #include <boost/algorithm/string/classification.hpp> // Include boost::for is_any_of
5 #include <boost/algorithm/string/split.hpp> // Include for boost::split
6 #include "larsim/MCCheater/ParticleInventoryService.h" // for printing
7 
8 
9 struct SortEntry{
10  unsigned int index;
11  float val;
12 };
13 
14 bool valDecreasing (SortEntry c1, SortEntry c2) { return (c1.val > c2.val);}
15 bool valIncreasing (SortEntry c1, SortEntry c2) { return (c1.val < c2.val);}
16 
17 namespace tca {
18 
19  // dressed muons - new function
20  void MakeHaloTj(TCSlice& slc, Trajectory& muTj, bool prt)
21  {
22  // Creates a "halo trajectory" around a muon tj consisting of hits and trajectories
23  // that are within MuonTag[4] distance. The halo tj is a virtual clone of muTj in the
24  // sense that it has the same number of points and the same start and end points.
25 
26  if(tcc.muonTag.size() < 5) return;
27  if(tcc.muonTag[4] <= 0) return;
28  if(!tcc.useAlg[kHaloTj]) return;
29 
30  if(muTj.PDGCode != 13) return;
31 
32  // check for daughter delta-rays
33  std::vector<int> dtrs;
34  for(auto& dtj : slc.tjs) {
35  if(dtj.AlgMod[kKilled]) continue;
36  if(dtj.ParentID != muTj.ID) continue;
37  dtrs.push_back(dtj.ID);
38  if(!dtj.AlgMod[kDeltaRay]) continue;
39  if(prt) std::cout<<"MakeHaloTj: Killing delta-ray T"<<dtj.ID<<"\n";
40  // Kill a delta-ray PFParticle?
41  if(dtj.AlgMod[kMat3D]) {
42  unsigned short pfpIndex = GetPFPIndex(slc, dtj.ID);
43  if(pfpIndex == USHRT_MAX) {
44  if(prt) std::cout<<" No PFP found for 3D-matched delta-ray\n";
45  } else {
46  auto& pfp = slc.pfps[pfpIndex];
47  if(prt) std::cout<<" Killing delta-ray PFParticle P"<<pfp.UID<<"\n";
48  pfp.ID = 0;
49  // correct the parent -> daughter assn
50  if(pfp.ParentUID > 0) {
51  auto parentIndx = GetSliceIndex("P", pfp.ParentUID);
52  if(parentIndx.first != USHRT_MAX) {
53  auto& parent = slices[parentIndx.first].pfps[parentIndx.second];
54  std::vector<int> newDtrUIDs;
55  for(auto uid : parent.DtrUIDs) if(uid != dtj.UID) newDtrUIDs.push_back(uid);
56  parent.DtrUIDs = newDtrUIDs;
57  } // parent found
58  } // correct the parent
59  } // kill PFParticle
60  } // kill
61  MakeTrajectoryObsolete(slc, (unsigned int)(dtj.ID - 1));
62  } // dtj
63 
64  // make a copy
65  Trajectory tj;
66  tj.CTP = muTj.CTP;
67  // We can't use StoreTraj so variables need to be defined here
68  tj.ID = slc.tjs.size() + 1;
69  tj.WorkID = muTj.WorkID;
70  // increment the global ID
71  ++evt.globalTjID;
72  tj.UID = evt.globalTjID;
73  tj.PDGCode = 11;
74  tj.Pass = muTj.Pass;
75  tj.StepDir = muTj.StepDir;
76  tj.StartEnd = muTj.StartEnd;
77  tj.TotChg = 0;
78  tj.ChgRMS = 0;
79  tj.EndPt[0] = 0;
80  tj.ParentID = muTj.ID;
81  tj.AlgMod.reset();
82  tj.AlgMod[kHaloTj] = true;
83  // start a list of tjs that have points near the muon
84  std::vector<int> closeTjs;
85  for(unsigned short ipt = muTj.EndPt[0]; ipt <= muTj.EndPt[1]; ++ipt) {
86  auto tp = muTj.Pts[ipt];
87  tp.Hits.resize(0);
88  tp.UseHit.reset();
89  tp.Chg = 0; tp.AveChg = 0; tp.ChgPull = 0;
90  tp.Delta = 0; tp.DeltaRMS = 0;
91  tp.FitChi = 0; tp.NTPsFit = 0;
92  float window = tcc.muonTag[4];
93  if(tp.Dir[0] != 0) window *= std::abs(1/tp.Dir[0]);
94  if(!FindCloseHits(slc, tp, window, kAllHits)) continue;
95  // add unused hits to the point and look for close tjs
96  bool hitsAdded = false;
97  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
98  unsigned int iht = tp.Hits[ii];
99  auto inTraj = slc.slHits[iht].InTraj;
100  if(inTraj < 0) continue;
101  if(inTraj == 0) {
102  tp.UseHit[ii] = true;
103  slc.slHits[iht].InTraj = tj.ID;
104  hitsAdded = true;
105  } else {
106  // add to the closeTjs list
107  if(inTraj != muTj.ID && std::find(closeTjs.begin(), closeTjs.end(), inTraj) == closeTjs.end()) closeTjs.push_back(inTraj);
108  }
109  } // ii
110  if(hitsAdded) {
111  DefineHitPos(slc, tp);
112  tp.Delta = PointTrajDOCA(slc, tp.HitPos[0], tp.HitPos[1], tp);
113  tj.TotChg += tp.Chg;
114  tj.Pts.push_back(tp);
115  } // hitsAdded
116  } // ipt
117  if(tj.Pts.empty()) return;
118  tj.EndPt[1] = tj.Pts.size() - 1;
119  if(prt) {
120  std::cout<<"MHTj: T"<<muTj.ID<<" npts "<<tj.Pts.size()<<" close";
121  for(auto tid : closeTjs) std::cout<<" T"<<tid;
122  std::cout<<"\n";
123  PrintTrajectory("DM", slc, tj, USHRT_MAX);
124  }
125  slc.tjs.push_back(tj);
126  } // MakeHaloTj
127 
129  void DefineTjParents(TCSlice& slc, bool prt)
130  {
131 /*
132  This function sets the ParentUID of Tjs in this tpcid to create a hierarchy. The highest Score
133  3D vertex in a chain of Tjs and vertices is declared the primary vertex; vx3.Primary = true. Tjs directly attached
134  to that vertex are declared Primary trajectories with ParentUID = 0. All other Tjs in the chain have ParentUID
135  set to the next upstream Tj to which it is attached by a vertex. In the graphical description below, V1 and V4 are
136  2D vertices that are matched to a high-score 3D vertex. The V1 Score is greater than the V2 Score and V3 Score.
137  V1 and V4 are declared to be primary vertices. T1, T2, T6 and T7 are declared to be primary Tjs
138 
139  V1 - T1 - V2 - T3 V4 - T6 / T8
140  \ \ /
141  T2 - V3 - T4 T7
142  \
143  T5
144 
145  This is represented as follows. The NeutrinoPrimaryTjID is defined by a function.
146  Tj ParentUID NeutrinoPrimaryTjID
147  -----------------------------------
148  T1 0 T1
149  T2 0 T2
150  T3 T1 T2
151  T4 T2 T2
152  T5 T2 T2
153  T6 0 -1
154  T7 0 -1
155  T8 -1 -1
156 */
157 
158  // don't do anything if this is test beam data
159  if(tcc.modes[kTestBeam]) return;
160 
161  // clear old information
162  for(auto& tj : slc.tjs) {
163  if(tj.AlgMod[kKilled]) continue;
164  // ignore delta rays
165  if(tj.AlgMod[kDeltaRay] || tj.AlgMod[kHaloTj]) continue;
166  tj.ParentID = 0;
167  } // tj
168 
169  // sort vertice by decreasing score
170  std::vector<int> temp;
171  for(auto& vx3 : slc.vtx3s) {
172  if(vx3.ID == 0) continue;
173  // clear the Primary flag while we are here
174  vx3.Primary = false;
175  temp.push_back(vx3.ID);
176  } // vx3
177  if(temp.empty()) return;
178 
179  // Make a master list of all Tjs that are attached to these vertices
180  std::vector<int> masterlist;
181  for(auto vx3id : temp) {
182  auto& vx3 = slc.vtx3s[vx3id - 1];
183  float score;
184  auto tjlist = GetVtxTjIDs(slc, vx3, score);
185  for(auto tjid : tjlist) {
186  // Temp? Check for an existing parentID
187  auto& tj = slc.tjs[tjid - 1];
188  if(tj.ParentID != 0) {
189 // std::cout<<"**** Tj "<<tj.ID<<" Existing parent "<<tj.ParentID<<" PDGCode "<<tj.PDGCode<<". with a vertex... \n";
190  tj.ParentID = 0;
191  }
192  if(std::find(masterlist.begin(), masterlist.end(), tjid) == masterlist.end()) masterlist.push_back(tjid);
193  } // tjid
194  } // vxid
195  if(prt) {
196  mf::LogVerbatim myprt("TC");
197  myprt<<"DTP: masterlist Tjs";
198  for(auto tjid : masterlist) myprt<<" "<<tjid;
199  }
200 
201  // Do the sort
202  std::vector<SortEntry> sortVec(temp.size());
203  for(unsigned short indx = 0; indx < temp.size(); ++indx) {
204  auto& vx3 = slc.vtx3s[temp[indx] - 1];
205  sortVec[indx].index = indx;
206  sortVec[indx].val = vx3.Score;
207  } // indx
208  if(sortVec.size() > 1) std::sort(sortVec.begin(), sortVec.end(), valDecreasing);
209  // put them into order
210  auto vlist = temp;
211  for(unsigned short indx = 0; indx < temp.size(); ++indx) vlist[indx] = temp[sortVec[indx].index];
212 
213  // make a neutrino PFParticle to associate with the highest score vertex if it is high enough
214  if(tcc.match3DCuts[0] > 0) {
215  auto& vx3 = slc.vtx3s[vlist[0] - 1];
216  if(vx3.Score > tcc.vtx2DCuts[7]) {
217  auto neutrinoPFP = CreatePFP(slc);
218  // call it the neutrino vertex
219  vx3.Neutrino = true;
220  // put the vertex at the end of the neutrino
221  neutrinoPFP.XYZ[1][0] = vx3.X;
222  neutrinoPFP.XYZ[1][1] = vx3.Y;
223  neutrinoPFP.XYZ[1][2] = vx3.Z;
224  neutrinoPFP.XYZ[0] = neutrinoPFP.XYZ[1];
225  neutrinoPFP.Dir[1][2] = 1;
226  neutrinoPFP.Dir[0][2] = 1;
227  // This may be set to 12 later on if a primary shower is reconstructed
228  neutrinoPFP.PDGCode = 14;
229  neutrinoPFP.Vx3ID[1] = vx3.ID;
230  neutrinoPFP.Vx3ID[0] = vx3.ID;
231  neutrinoPFP.NeedsUpdate = false;
232  // the rest of this will be defined later
233  if(!StorePFP(slc, neutrinoPFP)) return;
234  }
235  } // User wants to make PFParticles
236  // a temp vector to ensure that we only consider a vertex once
237  std::vector<bool> lookedAt3(slc.vtx3s.size() + 1, false);
238  std::vector<bool> lookedAt2(slc.vtxs.size() + 1, false);
239  // vector of parent-daughter pairs
240  std::vector<std::pair<int, int>> pardtr;
241  // Start with the highest score vertex
242  for(unsigned short indx = 0; indx < vlist.size(); ++indx) {
243  auto& vx3 = slc.vtx3s[vlist[indx] - 1];
244  if(lookedAt3[vx3.ID]) continue;
245  vx3.Primary = true;
246  lookedAt3[vx3.ID] = true;
247  // make a list of Tjs attached to this vertex
248  float score;
249  auto primTjList = GetVtxTjIDs(slc, vx3, score);
250  if(primTjList.empty()) continue;
251  pardtr.clear();
252  for(auto primTjID : primTjList) {
253  auto& primTj = slc.tjs[primTjID - 1];
254  // This isn't a primary tj if the parent ID isn't -1
255  if(primTj.ParentID != -1) continue;
256  if(prt) mf::LogVerbatim("TC")<<"Vx3 "<<vx3.ID<<" Primary tj "<<primTj.ID;
257  // declare this a primary tj
258  primTj.ParentID = 0;
259  // look for daughter tjs = those that are attached to a 2D vertex
260  // at the other end
261  for(unsigned short end = 0; end < 2; ++end) {
262  if(primTj.VtxID[end] == 0) continue;
263  auto& vx2 = slc.vtxs[primTj.VtxID[end] - 1];
264  if(vx2.Vx3ID == vx3.ID) continue;
265  // found a 2D vertex. Check for daughters
266  auto dtrList = GetVtxTjIDs(slc, vx2);
267  for(auto dtrID : dtrList) {
268  // ignore the primary tj
269  if(dtrID == primTjID) continue;
270  auto& dtj = slc.tjs[dtrID - 1];
271  if(dtj.ParentID != -1) {
272 // std::cout<<"DTP Error: dtr "<<dtrID<<" already has a parent "<<dtj.ParentID<<". Can't make it daughter of "<<primTjID<<"\n";
273  continue;
274  }
275  pardtr.push_back(std::make_pair(primTjID, dtrID));
276  if(prt) mf::LogVerbatim("TC")<<" primTj "<<primTjID<<" dtrID "<<dtrID;
277  } // tjid
278  } // end
279  // Ensure that end 0 of the trajectory is attached to the primary vertex
280  for(unsigned short end = 0; end < 2; ++end) {
281  if(primTj.VtxID[end] == 0) continue;
282  auto& vx2 = slc.vtxs[primTj.VtxID[end] - 1];
283  if(vx2.Vx3ID == vx3.ID && end != 0) ReverseTraj(slc, primTj);
284  } // end
285  } // tjid
286  if(pardtr.empty()) continue;
287  if(prt) {
288  mf::LogVerbatim myprt("TC");
289  myprt<<" par_dtr";
290  for(auto pdtr : pardtr) myprt<<" "<<pdtr.first<<"_"<<pdtr.second;
291  }
292  // iterate through the parent - daughter stack, removing the last pair when a
293  // ParentID is updated and adding pairs for new daughters
294  for(unsigned short nit = 0; nit < 100; ++nit) {
295  auto lastPair = pardtr[pardtr.size() - 1];
296  auto& dtj = slc.tjs[lastPair.second - 1];
297  dtj.ParentID = lastPair.first;
298  // reverse the daughter trajectory if necessary so that end 0 is closest to the parent
299  float doca = 100;
300  unsigned short dpt = 0, ppt = 0;
301  auto& ptj = slc.tjs[lastPair.first - 1];
302  // find the point on the daughter tj that is closest to the parent
303  TrajTrajDOCA(slc, dtj, ptj, dpt, ppt, doca);
304  // reverse the daughter if the closest point is near end 1 of the daughter
305  if(prt) mf::LogVerbatim("TC")<<"Set parent "<<ptj.ID<<" dtr "<<dtj.ID;
306  // remove that entry
307  pardtr.pop_back();
308  // Add entries for new daughters
309  for(unsigned short end = 0; end < 2; ++end) {
310  if(dtj.VtxID[end] == 0) continue;
311  auto& vx2 = slc.vtxs[dtj.VtxID[end] - 1];
312  if(lookedAt2[vx2.ID]) continue;
313  lookedAt2[vx2.ID] = true;
314  auto tjlist = GetVtxTjIDs(slc, vx2);
315  for(auto tjid : tjlist) {
316  if(tjid == dtj.ID || tjid == ptj.ID) continue;
317  pardtr.push_back(std::make_pair(dtj.ID, tjid));
318  if(prt) {
319  mf::LogVerbatim myprt("TC");
320  myprt<<" add par_dtr";
321  for(auto pdtr : pardtr) myprt<<" "<<pdtr.first<<"_"<<pdtr.second;
322  }
323  }
324  } // end
325  if(pardtr.empty()) break;
326  } // nit
327  } // indx
328  // check the master list
329  for(auto tjid : masterlist) {
330  auto& tj = slc.tjs[tjid - 1];
331  if(tj.ParentID < 0) {
332 // std::cout<<"Tj "<<tj.ID<<" is in the master list but doesn't have a Parent\n";
333  tj.ParentID = tj.ID;
334  }
335  } // tjid
336 
337  } // DefineTjParents
338 
340  float MaxChargeAsymmetry(TCSlice& slc, std::vector<int>& tjIDs)
341  {
342  // calculates the maximum charge asymmetry in all planes using the supplied list of Tjs
343  if(tjIDs.size() < 2) return 1;
344  std::vector<float> plnchg(slc.nPlanes);
345  for(auto tjid : tjIDs) {
346  if(tjid <= 0 || tjid > (int)slc.tjs.size()) return 1;
347  auto& tj = slc.tjs[tjid - 1];
348  if(tj.TotChg == 0) UpdateTjChgProperties("MCA", slc, tj, false);
349  unsigned short plane = DecodeCTP(tj.CTP).Plane;
350  plnchg[plane] += tj.TotChg;
351  } // tjid
352  float aveChg = 0;
353  float cnt = 0;
354  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
355  if(plnchg[plane] == 0) continue;
356  aveChg += plnchg[plane];
357  ++cnt;
358  } // plane
359  if(cnt < 2) return 1;
360  aveChg /= cnt;
361  float maxAsym = 0;
362  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
363  // ignore zeros
364  if(plnchg[plane] == 0) continue;
365  float asym = std::abs(plnchg[plane] - aveChg) / (plnchg[plane] + aveChg);
366  if(asym > maxAsym) maxAsym = asym;
367  } // plane
368  return maxAsym;
369  } // MaxChargeAsymmetry
370 
372  int PDGCodeVote(TCSlice& slc, std::vector<int>& tjIDs, bool prt)
373  {
374  // Returns the most likely PDGCode for the set of Tjs provided
375  // The PDG codes are:
376  // 0 = your basic track-like trajectory
377  // 11 = Tagged delta-ray
378  // 13 = Tagged muon
379  // 211 = pion-like. There exists a Bragg peak at an end with a vertex
380  // 2212 = proton-like. There exists a Bragg peak at an end without a vertex
381  std::array<int, 5> codeList = {{0, 11, 13, 111, 211}};
382  unsigned short codeIndex = 0;
383  if(tjIDs.empty()) return codeList[codeIndex];
384 
385  std::array<unsigned short, 5> cnts;
386  cnts.fill(0);
387  // Count Bragg peaks. This assumes that the Tjs are in order...
388 // std::array<unsigned short, 2> stopCnt {{0, 0}};
389  float maxLen = 0;
390  for(auto tjid : tjIDs) {
391  if(tjid <= 0 || tjid > (int)slc.tjs.size()) continue;
392  auto& tj = slc.tjs[tjid - 1];
393  for(unsigned short ii = 0; ii < 5; ++ii) if(tj.PDGCode == codeList[ii]) ++cnts[ii];
394  // count InShower Tjs with PDGCode not set (yet)
395 // if(tj.PDGCode != 11 && tj.AlgMod[kShowerLike]) ++cnts[1];
396 // for(unsigned short end = 0; end < 2; ++end) if(tj.StopFlag[end][kBragg]) ++stopCnt[end];
397  float len = TrajLength(tj);
398  if(len > maxLen) maxLen = len;
399  } // tjid
400  unsigned maxCnt = 0;
401  // ignore the first PDG code in the list (the default)
402  for(unsigned short ii = 1; ii < 5; ++ii) {
403  if(cnts[ii] > maxCnt) {
404  maxCnt = cnts[ii];
405  codeIndex = ii;
406  }
407  } // ii
408 
409  return codeList[codeIndex];
410  } // PDGCodeVote
411 
413  unsigned short NumDeltaRays(TCSlice& slc, const Trajectory& tj)
414  {
415  // returns the number of delta rays that have this tj as a parent
416  unsigned short cnt = 0;
417  for(auto& dtj : slc.tjs) {
418  if(dtj.AlgMod[kKilled] || dtj.AlgMod[kHaloTj]) continue;
419  if(!dtj.AlgMod[kDeltaRay]) continue;
420  if(dtj.ParentID == tj.ID) ++cnt;
421  } // tj
422  return cnt;
423  } // NumDeltaRays
424 
426  unsigned short NumDeltaRays(TCSlice& slc, std::vector<int>& tjIDs)
427  {
428  // Count the number of delta-rays that have a Tj in the list of TjIDs as a parent.
429  if(tjIDs.empty()) return 0;
430  if(tjIDs[0] <= 0 || tjIDs[0] > (int)slc.tjs.size()) return 0;
431  unsigned short cnt = 0;
432  for(auto& tj : slc.tjs) {
433  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
434  if(!tj.AlgMod[kDeltaRay]) continue;
435  if(std::find(tjIDs.begin(), tjIDs.end(), tj.ParentID) != tjIDs.end()) ++cnt;
436  } // tj
437  return cnt;
438  } // NumDeltaRays
439 
442  {
443  // Returns the ID of the grandparent of this tj that is a primary tj that is attached
444  // to the neutrino vertex. 0 is returned if this condition is not met.
445  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return -1;
446  if(tj.ParentID <= 0) return -1;
447  int primID = PrimaryID(slc, tj);
448  if(primID <= 0 || primID > (int)slc.tjs.size()) return -1;
449 
450  // We have the ID of the primary tj. Now see if it is attached to the neutrino vertex
451  auto& ptj = slc.tjs[primID - 1];
452  for(unsigned short end = 0; end < 2; ++end) {
453  if(ptj.VtxID[end] == 0) continue;
454  auto& vx2 = slc.vtxs[ptj.VtxID[end] - 1];
455  if(vx2.Vx3ID == 0) continue;
456  auto& vx3 = slc.vtx3s[vx2.Vx3ID - 1];
457  if(vx3.Neutrino) return primID;
458  } // end
459  return -1;
460  } // NeutrinoPrimaryTjUID
461 
463  int PrimaryID(TCSlice& slc, const Trajectory& tj)
464  {
465  // Returns the ID of the grandparent trajectory of this trajectory that is a primary
466  // trajectory (i.e. whose ParentID = 0).
467  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return -1;
468  if(tj.ParentID < 0 || tj.ParentID > (int)slc.tjs.size()) return -1;
469  if(tj.ParentID == 0) return tj.ID;
470  int parid = tj.ParentID;
471  for(unsigned short nit = 0; nit < 10; ++nit) {
472  if(parid < 1 || parid > (int)slc.tjs.size()) break;
473  auto& tj = slc.tjs[parid - 1];
474  if(tj.ParentID < 0 || tj.ParentID > (int)slc.tjs.size()) return -1;
475  if(tj.ParentID == 0) return tj.ID;
476  parid = tj.ParentID;
477  } // nit
478  return -1;
479  } // PrimaryID
480 
482  int PrimaryUID(TCSlice& slc, const PFPStruct& pfp)
483  {
484  // returns the UID of the most upstream PFParticle (that is not a neutrino)
485 
486  if(int(pfp.ParentUID) == pfp.UID || pfp.ParentUID <= 0) return pfp.ID;
487  int paruid = pfp.ParentUID;
488  int dtruid = pfp.UID;
489  unsigned short nit = 0;
490  while(true) {
491  auto slcIndx = GetSliceIndex("P", paruid);
492  auto& parent = slices[slcIndx.first].pfps[slcIndx.second];
493  // found a neutrino
494  if(parent.PDGCode == 14 || parent.PDGCode == 12) return dtruid;
495  // found a primary PFParticle?
496  if(parent.ParentUID == 0) return parent.UID;
497  if(int(parent.ParentUID) == parent.UID) return parent.UID;
498  dtruid = parent.UID;
499  paruid = parent.ParentUID;
500  if(paruid < 0) return 0;
501  ++nit;
502  if(nit == 10) return 0;
503  }
504  } // PrimaryUID
505 
507  bool MergeTjIntoPFP(TCSlice& slc, int mtjid, PFPStruct& pfp, bool prt)
508  {
509  // Tries to merge Tj with ID tjid into PFParticle pfp
510  if(mtjid > (int)slc.tjs.size()) return false;
511  auto& mtj = slc.tjs[mtjid - 1];
512  // find the Tj in pfp.TjIDs which it should be merged with
513  int otjid = 0;
514  for(auto tjid : pfp.TjIDs) {
515  auto& otj = slc.tjs[tjid - 1];
516  if(otj.CTP == mtj.CTP) {
517  otjid = tjid;
518  break;
519  }
520  } // tjid
521  if(otjid == 0) return false;
522  if(MergeAndStore(slc, otjid - 1, mtjid - 1, prt)) {
523  int newtjid = slc.tjs.size();
524  if(prt) mf::LogVerbatim("TC")<<"MergeTjIntoPFP: merged T"<<otjid<<" with T"<<mtjid<<" -> T"<<newtjid;
525  std::replace(pfp.TjIDs.begin(), pfp.TjIDs.begin(), otjid, newtjid);
526  return true;
527  } else {
528  if(prt) mf::LogVerbatim("TC")<<"MergeTjIntoPFP: merge T"<<otjid<<" with T"<<mtjid<<" failed ";
529  return false;
530  }
531  } // MergeTjIntoPFP
532 
534  bool CompatibleMerge(TCSlice& slc, std::vector<int>& tjIDs, bool prt)
535  {
536  // Returns true if the last Tj in tjIDs has a topology consistent with it being
537  // merged with other Tjs in the same plane in the list. This is done by requiring that
538  // the closest TP between the last Tj and any other Tj is EndPt[0] or EndPt[1]. This is
539  // shown graphically here where the numbers represent the ID of a Tj that has a TP on a wire.
540  // Assume that TjIDs = {1, 2, 3, 4, 7} where T1 and T3 are in plane 0, T2 is in plane 1 and
541  // T4 is in plane 2. T7, in plane 0, was added to TjIDs with the intent of merging it with
542  // T1 and T3 into a single trajectory. This is a compatible merge if Tj7 has the following
543  // topology:
544  // 111111 333333 7777777
545  // This is an incompatible topology
546  // 111111 333333
547  // 7777777777
548  if(tjIDs.size() < 2) return false;
549  unsigned short lasttj = tjIDs[tjIDs.size() - 1] - 1;
550  auto& mtj = slc.tjs[lasttj];
551  bool mtjIsShort = (mtj.Pts.size() < 5);
552  // minimum separation from each end of mtj
553  std::array<float, 2> minsep2 {{1000, 1000}};
554  // ID of the Tj with the minimum separation
555  std::array<int, 2> minsepTj {{0, 0}};
556  // and the index of the point on that Tj
557  std::array<unsigned short, 2> minsepPt;
558  // determine the end of the closest Tj point. Start by assuming
559  // the closest Tj point is not near an end (end = 0);
560  std::array<unsigned short, 2> minsepEnd;
561  for(auto tjid : tjIDs) {
562  auto& tj = slc.tjs[tjid - 1];
563  if(tj.CTP != mtj.CTP) continue;
564  if(tj.ID == mtj.ID) continue;
565  for(unsigned short mend = 0; mend < 2; ++mend) {
566  Point2_t mendPos = mtj.Pts[mtj.EndPt[mend]].Pos;
567  float sep2 = minsep2[mend];
568  unsigned short closePt = 0;
569  if(!TrajClosestApproach(tj, mendPos[0], mendPos[1], closePt, sep2)) continue;
570  minsep2[mend] = sep2;
571  minsepTj[mend] = tjid;
572  minsepPt[mend] = closePt;
573  // set the end to a bogus value (not near an end)
574  minsepEnd[mend] = 2;
575  short dend0 = abs((short)closePt - tj.EndPt[0]);
576  short dend1 = abs((short)closePt - tj.EndPt[1]);
577  if(dend0 < dend1 && dend0 < 3) minsepEnd[mend] = 0;
578  if(dend1 < dend0 && dend1 < 3) minsepEnd[mend] = 1;
579  } // mend
580  } // tjid
581 // bool isCompatible = (minsepEnd[0] != 2 && minsepTj[0] == minsepTj[1] && minsepEnd[0] == minsepEnd[1]);
582  // don't require that the minsepTjs be the same. This would reject this topology
583  // 111111 333333 7777777
584  // if mtj.ID = 3
585  bool isCompatible = (minsepEnd[0] != 2 && minsepEnd[1] != 2);
586  // check for large separation between the closest points for short Tjs
587  if(isCompatible && mtjIsShort) {
588  float minminsep = minsep2[0];
589  if(minsep2[1] < minminsep) minminsep = minsep2[1];
590  // require that the separation be less than sqrt(5)
591  isCompatible = minminsep < 5;
592 // if(minminsep > 2) std::cout<<"CM: mtj T"<<mtj.ID<<" is short and the separation is large "<<minminsep<<". Check for signal between\n ";
593  }
594  if(prt) {
595  mf::LogVerbatim myprt("TC");
596  myprt<<"CompatibleMerge: T"<<mtj.ID<<" end";
597  for(unsigned short end = 0; end < 2; ++end) myprt<<" T"<<minsepTj[end]<<"_I"<<minsepPt[end]<<"_E"<<minsepEnd[end]<<" minsep "<<sqrt(minsep2[end]);
598  myprt<<" Compatible? "<<isCompatible;
599  } // prt
600  return isCompatible;
601 
602  } // CompatibleMerge
603 
605  bool CompatibleMerge(TCSlice& slc, const Trajectory& tj1, const Trajectory& tj2, bool prt)
606  {
607  // returns true if the two Tjs are compatible with and end0-end1 merge. This function has many aspects of the
608  // compatibility checks done in EndMerge but with looser cuts.
609  if(tj1.AlgMod[kKilled] || tj2.AlgMod[kKilled]) return false;
610  if(tj1.AlgMod[kHaloTj] || tj2.AlgMod[kHaloTj]) return false;
611  if(tj1.CTP != tj2.CTP) return false;
612  unsigned short end1 = -1, end2 = 0;
613  float minLen = PosSep(tj1.Pts[tj1.EndPt[0]].Pos, tj1.Pts[tj1.EndPt[1]].Pos);
614  float len2 = PosSep(tj2.Pts[tj2.EndPt[0]].Pos, tj2.Pts[tj2.EndPt[1]].Pos);
615  if(len2 < minLen) minLen = len2;
616  minLen *= 1.2;
617  if(minLen > 10) minLen = 10;
618  for(unsigned short e1 = 0; e1 < 2; ++e1) {
619  auto& tp1 = tj1.Pts[tj1.EndPt[e1]];
620  for(unsigned short e2 = 0; e2 < 2; ++e2) {
621  auto& tp2 = tj2.Pts[tj2.EndPt[e2]];
622  float sep = PosSep(tp1.Pos, tp2.Pos);
623  if(sep < minLen) {
624  minLen = sep;
625  end1 = e1; end2 = e2;
626  }
627  } // e2
628  } // e1
629  if(end1 < 0) return false;
630  // require end to end
631  if(end2 != 1 - end1) return false;
632 
633  float overlapFraction = OverlapFraction(slc, tj1, tj2);
634  if(overlapFraction > 0.25) {
635  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" overlapFraction "<<overlapFraction<<" > 0.25 ";
636  return false;
637  }
638 
639  auto& tp1 = tj1.Pts[tj1.EndPt[end1]];
640  auto& tp2 = tj2.Pts[tj2.EndPt[end2]];
641 /* This causes problems with hit collections that have cosmics removed
642  if(!SignalBetween(slc, tp1, tp2, 0.8, false)) {
643  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" no signal between these points "<<PrintPos(slc, tp1.Pos)<<" "<<PrintPos(slc, tp2.Pos);
644  return false;
645  }
646 */
647  float doca1 = PointTrajDOCA(slc, tp1.Pos[0], tp1.Pos[1], tp2);
648  float doca2 = PointTrajDOCA(slc, tp2.Pos[0], tp2.Pos[1], tp1);
649  if(doca1 > 2 && doca2 > 2) {
650  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" Both docas > 2 "<<doca1<<" "<<doca2;
651  return false;
652  }
653 
654  float dang = DeltaAngle(tp1.Ang, tp2.Ang);
655  if(dang > 2 * tcc.kinkCuts[0]) {
656  if(prt) mf::LogVerbatim("TC")<<"CM: "<<tj1.ID<<" "<<tj2.ID<<" dang "<<dang<<" > "<<2 * tcc.kinkCuts[0];
657  return false;
658  }
659 
660  return true;
661  } // CompatibleMerge
662 
664  float OverlapFraction(TCSlice& slc, const Trajectory& tj1, const Trajectory& tj2)
665  {
666  // returns the fraction of wires spanned by two trajectories
667  float minWire = 1E6;
668  float maxWire = -1E6;
669 
670  float cnt1 = 0;
671  for(auto& tp : tj1.Pts) {
672  if(tp.Chg == 0) continue;
673  if(tp.Pos[0] < 0) continue;
674  if(tp.Pos[0] < minWire) minWire = tp.Pos[0];
675  if(tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
676  ++cnt1;
677  }
678  if(cnt1 == 0) return 0;
679  float cnt2 = 0;
680  for(auto& tp : tj2.Pts) {
681  if(tp.Chg == 0) continue;
682  if(tp.Pos[0] < 0) continue;
683  if(tp.Pos[0] < minWire) minWire = tp.Pos[0];
684  if(tp.Pos[0] > maxWire) maxWire = tp.Pos[0];
685  ++cnt2;
686  }
687  if(cnt2 == 0) return 0;
688  int span = maxWire - minWire;
689  if(span <= 0) return 0;
690  std::vector<unsigned short> wcnt(span);
691  for(auto& tp : tj1.Pts) {
692  if(tp.Chg == 0) continue;
693  if(tp.Pos[0] < -0.4) continue;
694  int indx = std::nearbyint(tp.Pos[0] - minWire);
695  if(indx < 0 || indx > span - 1) continue;
696  ++wcnt[indx];
697  }
698  for(auto& tp : tj2.Pts) {
699  if(tp.Chg == 0) continue;
700  if(tp.Pos[0] < -0.4) continue;
701  int indx = std::nearbyint(tp.Pos[0] - minWire);
702  if(indx < 0 || indx > span - 1) continue;
703  ++wcnt[indx];
704  }
705  float cntOverlap = 0;
706  for(auto cnt : wcnt) if(cnt > 1) ++cntOverlap;
707  if(cnt1 < cnt2) {
708  return cntOverlap / cnt1;
709  } else {
710  return cntOverlap / cnt2;
711  }
712 
713  } // OverlapFraction
714 
716  unsigned short AngleRange(TrajPoint const& tp)
717  {
718  return AngleRange(tp.Ang);
719  }
720 
723  {
724  unsigned short ar = AngleRange(tp.Ang);
725  if(ar == tcc.angleRanges.size() - 1) {
726  // Very large angle
727  tp.AngleCode = 2;
728  } else if(tcc.angleRanges.size() > 2 && ar == tcc.angleRanges.size() - 2) {
729  // Large angle
730  tp.AngleCode = 1;
731  } else {
732  // Small angle
733  tp.AngleCode = 0;
734  }
735 
736  } // SetAngleCode
737 
739  unsigned short AngleRange(float angle)
740  {
741  // returns the index of the angle range
742  if(angle > M_PI) angle = M_PI;
743  if(angle < -M_PI) angle = M_PI;
744  if(angle < 0) angle = -angle;
745  if(angle > M_PI/2) angle = M_PI - angle;
746  for(unsigned short ir = 0; ir < tcc.angleRanges.size(); ++ir) {
747  if(angle < tcc.angleRanges[ir]) return ir;
748  }
749  return tcc.angleRanges.size() - 1;
750  } // AngleRange
751 
753  void FitTraj(TCSlice& slc, Trajectory& tj)
754  {
755  // Jacket around FitTraj to fit the leading edge of the supplied trajectory
756  unsigned short originPt = tj.EndPt[1];
757  unsigned short npts = tj.Pts[originPt].NTPsFit;
758  TrajPoint tpFit;
759  unsigned short fitDir = -1;
760  FitTraj(slc, tj, originPt, npts, fitDir, tpFit);
761  tj.Pts[originPt] = tpFit;
762 
763  } // FitTraj
764 
766  void FitTraj(TCSlice& slc, Trajectory& tj, unsigned short originPt, unsigned short npts, short fitDir, TrajPoint& tpFit)
767  {
768  // Fit the supplied trajectory using HitPos positions with the origin at originPt.
769  // The npts is interpreted as the number of points on each side of the origin
770  // The allowed modes are as follows, where i denotes a TP that is included, . denotes
771  // a TP with no hits, and x denotes a TP that is not included
772  //TP 012345678 fitDir originPt npts
773  // Oiiixxxxx 1 0 4 << npts in the fit
774  // xi.iiOxxx -1 5 4
775  // xiiiOiiix 0 4 4 << 2 * npts + 1 points in the fit
776  // xxxiO.ixx 0 4 1
777  // 0iiixxxxx 0 0 4
778  // This routine puts the results into tp if the fit is successfull. The
779  // fit "direction" is in increasing order along the trajectory from 0 to tj.Pts.size() - 1.
780 
781  // static const float twoPi = 2 * M_PI;
782 
783  if(originPt > tj.Pts.size() - 1) {
784  mf::LogWarning("TC")<<"FitTraj: Requesting fit of invalid TP "<<originPt;
785  return;
786  }
787 
788  // copy the origin TP into the fit TP
789  tpFit = tj.Pts[originPt];
790  // Assume that the fit will fail
791  tpFit.FitChi = 999;
792  if(fitDir < -1 || fitDir > 1) return;
793 
794  std::vector<double> x, y;
795  Point2_t origin = tj.Pts[originPt].HitPos;
796  // Use TP position if there aren't any hits on it
797  if(tj.Pts[originPt].Chg == 0) origin = tj.Pts[originPt].Pos;
798 
799  // simple two point case
800  if(NumPtsWithCharge(slc, tj, false) == 2) {
801  for(unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1]; ++ipt) {
802  if(tj.Pts[ipt].Chg == 0) continue;
803  double xx = tj.Pts[ipt].HitPos[0] - origin[0];
804  double yy = tj.Pts[ipt].HitPos[1] - origin[1];
805  x.push_back(xx);
806  y.push_back(yy);
807  } // ii
808  if(x.size() != 2) return;
809  if(x[0] == x[1]) {
810  // Either + or - pi/2
811  tpFit.Ang = M_PI/2;
812  if(y[1] < y[0]) tpFit.Ang = -tpFit.Ang;
813  } else {
814  double dx = x[1] - x[0];
815  double dy = y[1] - y[0];
816  tpFit.Ang = atan2(dy, dx);
817  }
818  tpFit.Dir[0] = cos(tpFit.Ang);
819  tpFit.Dir[1] = sin(tpFit.Ang);
820  tpFit.Pos[0] += origin[0];
821  tpFit.Pos[1] += origin[1];
822  tpFit.AngErr = 0.01;
823  tpFit.FitChi = 0.01;
824  SetAngleCode(tpFit);
825  return;
826  } // two points
827 
828  std::vector<double> w, q;
829  std::array<double, 2> dir;
830  double xx, yy, xr, yr;
831  double chgWt;
832 
833  // Rotate the traj hit position into the coordinate system defined by the
834  // originPt traj point, where x = along the trajectory, y = transverse
835  double rotAngle = tj.Pts[originPt].Ang;
836  double cs = cos(-rotAngle);
837  double sn = sin(-rotAngle);
838 
839  // enter the originPT hit info if it exists
840  if(tj.Pts[originPt].Chg > 0) {
841  xx = tj.Pts[originPt].HitPos[0] - origin[0];
842  yy = tj.Pts[originPt].HitPos[1] - origin[1];
843  xr = cs * xx - sn * yy;
844  yr = sn * xx + cs * yy;
845  x.push_back(xr);
846  y.push_back(yr);
847  chgWt = tj.Pts[originPt].ChgPull;
848  if(chgWt < 1) chgWt = 1;
849  chgWt *= chgWt;
850  w.push_back(chgWt * tj.Pts[originPt].HitPosErr2);
851  }
852 
853  // correct npts to account for the origin point
854  if(fitDir != 0) --npts;
855 
856  // step in the + direction first
857  if(fitDir != -1) {
858  unsigned short cnt = 0;
859  for(unsigned short ipt = originPt + 1; ipt < tj.Pts.size(); ++ipt) {
860  if(tj.Pts[ipt].Chg == 0) continue;
861  xx = tj.Pts[ipt].HitPos[0] - origin[0];
862  yy = tj.Pts[ipt].HitPos[1] - origin[1];
863  xr = cs * xx - sn * yy;
864  yr = sn * xx + cs * yy;
865  x.push_back(xr);
866  y.push_back(yr);
867  chgWt = tj.Pts[ipt].ChgPull;
868  if(chgWt < 1) chgWt = 1;
869  chgWt *= chgWt;
870  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
871  ++cnt;
872  if(cnt == npts) break;
873  } // ipt
874  } // fitDir != -1
875 
876  // step in the - direction next
877  if(fitDir != 1 && originPt > 0) {
878  unsigned short cnt = 0;
879  for(unsigned short ii = 1; ii < tj.Pts.size(); ++ii) {
880  unsigned short ipt = originPt - ii;
881  if(ipt > tj.Pts.size() - 1) continue;
882  if(tj.Pts[ipt].Chg == 0) continue;
883  xx = tj.Pts[ipt].HitPos[0] - origin[0];
884  yy = tj.Pts[ipt].HitPos[1] - origin[1];
885  xr = cs * xx - sn * yy;
886  yr = sn * xx + cs * yy;
887  x.push_back(xr);
888  y.push_back(yr);
889  chgWt = tj.Pts[ipt].ChgPull;
890  if(chgWt < 1) chgWt = 1;
891  chgWt *= chgWt;
892  w.push_back(chgWt * tj.Pts[ipt].HitPosErr2);
893  ++cnt;
894  if(cnt == npts) break;
895  if(ipt == 0) break;
896  } // ipt
897  } // fitDir != -1
898 
899  // Not enough points to define a line?
900  if(x.size() < 2) return;
901 
902  double sum = 0.;
903  double sumx = 0.;
904  double sumy = 0.;
905  double sumxy = 0.;
906  double sumx2 = 0.;
907  double sumy2 = 0.;
908 
909  // weight by the charge ratio and accumulate sums
910  double wght;
911  for(unsigned short ipt = 0; ipt < x.size(); ++ipt) {
912  if(w[ipt] < 0.00001) w[ipt] = 0.00001;
913  wght = 1 / w[ipt];
914  sum += wght;
915  sumx += wght * x[ipt];
916  sumy += wght * y[ipt];
917  sumx2 += wght * x[ipt] * x[ipt];
918  sumy2 += wght * y[ipt] * y[ipt];
919  sumxy += wght * x[ipt] * y[ipt];
920  }
921  // calculate coefficients and std dev
922  double delta = sum * sumx2 - sumx * sumx;
923  if(delta == 0) return;
924  // A is the intercept
925  double A = (sumx2 * sumy - sumx * sumxy) / delta;
926  // B is the slope
927  double B = (sumxy * sum - sumx * sumy) / delta;
928 
929  // The chisq will be set below if there are enough points. Don't allow it to be 0
930  // so we can take Chisq ratios later
931  tpFit.FitChi = 0.01;
932  double newang = atan(B);
933  dir[0] = cos(newang);
934  dir[1] = sin(newang);
935  // rotate back into the (w,t) coordinate system
936  cs = cos(rotAngle);
937  sn = sin(rotAngle);
938  tpFit.Dir[0] = cs * dir[0] - sn * dir[1];
939  tpFit.Dir[1] = sn * dir[0] + cs * dir[1];
940  // ensure that the direction is consistent with the originPt direction
941  bool flipDir = false;
942  if(AngleRange(tj.Pts[originPt]) > 0) {
943  flipDir = std::signbit(tpFit.Dir[1]) != std::signbit(tj.Pts[originPt].Dir[1]);
944  } else {
945  flipDir = std::signbit(tpFit.Dir[0]) != std::signbit(tj.Pts[originPt].Dir[0]);
946  }
947  if(flipDir) {
948  tpFit.Dir[0] = -tpFit.Dir[0];
949  tpFit.Dir[1] = -tpFit.Dir[1];
950  }
951  tpFit.Ang = atan2(tpFit.Dir[1], tpFit.Dir[0]);
952  SetAngleCode(tpFit);
953  // if(prt) mf::LogVerbatim("TC")<<"FitTraj "<<originPt<<" originPt Dir "<<tj.Pts[originPt].Dir[0]<<" "<<tj.Pts[originPt].Dir[1]<<" rotAngle "<<rotAngle<<" tpFit.Dir "<<tpFit.Dir[0]<<" "<<tpFit.Dir[1]<<" Ang "<<tpFit.Ang<<" flipDir "<<flipDir<<" fit vector size "<<x.size();
954 
955  // rotate (0, intcpt) into (W,T) coordinates
956  tpFit.Pos[0] = -sn * A + origin[0];
957  tpFit.Pos[1] = cs * A + origin[1];
958  // force the origin to be at origin[0]
959  if(tpFit.AngleCode < 2) MoveTPToWire(tpFit, origin[0]);
960 
961  if(x.size() < 3) return;
962 
963  // Calculate chisq/DOF
964  double ndof = x.size() - 2;
965  double varnce = (sumy2 + A*A*sum + B*B*sumx2 - 2 * (A*sumy + B*sumxy - A*B*sumx)) / ndof;
966  if(varnce > 0.) {
967  // Intercept error is not used
968  // InterceptError = sqrt(varnce * sumx2 / delta);
969  double slopeError = sqrt(varnce * sum / delta);
970  tpFit.AngErr = std::abs(atan(slopeError));
971  } else {
972  tpFit.AngErr = 0.01;
973  }
974  sum = 0;
975  // calculate chisq
976  double arg;
977  for(unsigned short ii = 0; ii < y.size(); ++ii) {
978  arg = y[ii] - A - B * x[ii];
979  sum += arg * arg / w[ii];
980  }
981  tpFit.FitChi = sum / ndof;
982 
983  } // FitTraj
984 
986  float TjDirFOM(TCSlice& slc, const Trajectory& tj, bool prt)
987  {
988  // Calculate a FOM for the tj to be going from EndPt[0] -> EndPt[1] (FOM = 1)
989  // or EndPt[1] -> EndPt[0] (FOM = -1) by finding the overall charge slope, weighted
990  // by the presence of nearby InShower Tjs
991  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return 0;
992  if(tj.EndPt[1] - tj.EndPt[0] < 8) return 0;
993 
994  std::vector<double> x, y;
995  Point2_t origin = tj.Pts[tj.EndPt[0]].HitPos;
996  std::vector<double> w, q;
997 
998  unsigned short firstPt = tj.EndPt[0] + 2;
999  unsigned short lastPt = tj.EndPt[1] - 2;
1000  if(lastPt < firstPt + 3) return 0;
1001  // don't include end points
1002  for(unsigned short ipt = firstPt; ipt <= lastPt; ++ipt) {
1003  auto& tp = tj.Pts[ipt];
1004  if(tp.Chg <= 0) continue;
1005  // only consider points that don't overlap with other tjs
1006  if(tp.Environment[kEnvOverlap]) continue;
1007  double sep = PosSep(tp.Pos, origin);
1008  x.push_back(sep);
1009  y.push_back((double)tp.Chg);
1010  double wght = 0.2 * tp.Chg;
1011  w.push_back(wght * wght);
1012  } // tp
1013  if(w.size() < 3) return 0;
1014 
1015  double sum = 0.;
1016  double sumx = 0.;
1017  double sumy = 0.;
1018  double sumxy = 0.;
1019  double sumx2 = 0.;
1020  double sumy2 = 0.;
1021 
1022  // weight by the charge ratio and accumulate sums
1023  double wght;
1024  for(unsigned short ipt = 0; ipt < x.size(); ++ipt) {
1025  wght = 1 / w[ipt];
1026  sum += wght;
1027  sumx += wght * x[ipt];
1028  sumy += wght * y[ipt];
1029  sumx2 += wght * x[ipt] * x[ipt];
1030  sumy2 += wght * y[ipt] * y[ipt];
1031  sumxy += wght * x[ipt] * y[ipt];
1032  }
1033  // calculate coefficients and std dev
1034  double delta = sum * sumx2 - sumx * sumx;
1035  if(delta == 0) return 0;
1036  // A is the intercept
1037  double A = (sumx2 * sumy - sumx * sumxy) / delta;
1038  // B is the slope
1039  double B = (sumxy * sum - sumx * sumy) / delta;
1040 
1041  // Calculate the FOM
1042  double ndof = x.size() - 2;
1043  double varnce = (sumy2 + A*A*sum + B*B*sumx2 - 2 * (A*sumy + B*sumxy - A*B*sumx)) / ndof;
1044  if(varnce <= 0) return 0;
1045  double BErr = sqrt(varnce * sum / delta);
1046  // scale the error so that the significance is +/-1 when the slope is 3 * error
1047  // Note that the error is correct only if the average Chi/DOF = 1 which is probably not the case
1048  float slopeSig = B / (3 * BErr);
1049  if(slopeSig > 1) slopeSig = 1;
1050  if(slopeSig < -1) slopeSig = -1;
1051  // rescale it to be in the range of 0 - 1
1052  slopeSig = (1 + slopeSig) / 2;
1053 
1054  if(prt) {
1055  mf::LogVerbatim myprt("TC");
1056  myprt<<"TjDir: T"<<tj.ID<<" slope "<<std::fixed<<std::setprecision(1)<<B<<" error "<<BErr<<" DirFOM "<<slopeSig;
1057  myprt<<" using points from "<<PrintPos(slc, tj.Pts[firstPt])<<" "<<PrintPos(slc, tj.Pts[lastPt]);
1058  } // prt
1059  return slopeSig;
1060 
1061  } // TjDirFOM
1062 /*
1064  void WatchHit(std::string someText, TCSlice& slc)
1065  {
1066  // a temp routine to watch when inTraj changes for the supplied hit index, watchHit
1067 
1068  unsigned int wHit = 2001;
1069  static int wInTraj = 0;
1070 
1071  if(wHit < slc.slHits.size() && slc.slHits[wHit].InTraj != wInTraj) {
1072  std::cout<<someText<<" Hit "<<PrintHitShort(slc.slHits[wHit])<<" was InTraj T"<<wInTraj<<" now InTraj T"<<slc.slHits[wHit].InTraj<<" ntjs "<<slc.tjs.size()<<"\n";
1073  wInTraj = slc.slHits[wHit].InTraj;
1074  }
1075  } // WatchHit
1076 */
1078  void Reverse3DMatchTjs(TCSlice& slc, PFPStruct& pfp, bool prt)
1079  {
1080  // Return true if the 3D matched hits in the trajectories in slc.pfps are in the wrong order in terms of the
1081  // physics standpoint, e.g. dQ/dx, muon delta-ray tag, cosmic rays entering the detector, etc.
1082 
1083  // Don't reverse showers
1084  if(pfp.PDGCode == 1111) return;
1085 
1086  bool reverseMe = false;
1087 
1088  // look for stopping Tjs for contained PFParticles
1089  if(!reverseMe) {
1090  unsigned short braggCnt0 = 0;
1091  unsigned short braggCnt1 = 0;
1092  for(auto& tjID : pfp.TjIDs) {
1093  auto& tj = slc.tjs[tjID - 1];
1094  if(tj.StopFlag[0][kBragg]) ++braggCnt0;
1095  if(tj.StopFlag[1][kBragg]) ++braggCnt1;
1096  }
1097  if(braggCnt0 > 0 || braggCnt1 > 0) {
1098  pfp.PDGCode = 2212;
1099  // Vote for a Bragg peak at the beginning. It should be at the end
1100  if(braggCnt0 > braggCnt1) reverseMe = true;
1101  } // found a Bragg Peak
1102  } // look for stopping Tjs
1103 
1104  if(!reverseMe) return;
1105 
1106  // All of the trajectories should be reversed
1107  for(auto& tjID : pfp.TjIDs) {
1108  unsigned short itj = tjID - 1;
1109  Trajectory& tj = slc.tjs[itj];
1110  tj.AlgMod[kMat3D] = false;
1111  ReverseTraj(slc, tj);
1112  tj.AlgMod[kMat3D] = true;
1113  } // tjID
1114  // swap the matchVec end info also
1115  std::swap(pfp.XYZ[0], pfp.XYZ[1]);
1116  std::swap(pfp.Dir[0], pfp.Dir[1]);
1117  std::swap(pfp.DirErr[0], pfp.DirErr[1]);
1118  std::swap(pfp.dEdx[0], pfp.dEdx[1]);
1119  std::swap(pfp.dEdxErr[0], pfp.dEdxErr[1]);
1120  std::swap(pfp.Vx3ID[0], pfp.Vx3ID[1]);
1121 
1122  return;
1123 
1124  } // Reverse3DMatchTjs
1125 
1127  unsigned short GetPFPIndex(TCSlice& slc, int tjID)
1128  {
1129  if(slc.pfps.empty()) return USHRT_MAX;
1130  for(unsigned int ipfp = 0; ipfp < slc.pfps.size(); ++ipfp) {
1131  const auto& pfp = slc.pfps[ipfp];
1132  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tjID) != pfp.TjIDs.end()) return ipfp;
1133  } // indx
1134  return USHRT_MAX;
1135  } // GetPFPIndex
1136 
1138  unsigned short MatchVecIndex(TCSlice& slc, int tjID)
1139  {
1140  // returns the index into the tjs.matchVec vector of the first 3D match that
1141  // includes tjID
1142  for(unsigned int ims = 0; ims < slc.matchVec.size(); ++ims) {
1143  const auto& ms = slc.matchVec[ims];
1144  if(std::find(ms.TjIDs.begin(), ms.TjIDs.end(), tjID) != ms.TjIDs.end()) return ims;
1145  } // indx
1146  return USHRT_MAX;
1147  } // MatchVecIndex
1148 
1151  {
1152  // Sets InTraj[] = 0 for all TPs in work. Called when abandoning work
1153  for(auto& tp : tj.Pts) {
1154  for(auto iht : tp.Hits) {
1155  if(slc.slHits[iht].InTraj == tj.ID) slc.slHits[iht].InTraj = 0;
1156  }
1157  } // tp
1158 
1159  } // ReleaseWorkHits
1160 
1163  {
1164  // Sets InTraj = 0 and UseHit false for all used hits in tp
1165  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1166  if(tp.UseHit[ii]) {
1167  slc.slHits[tp.Hits[ii]].InTraj = 0;
1168  tp.UseHit[ii] = false;
1169  } // UseHit
1170  } // ii
1171  tp.Chg = 0;
1172  } // UnsetUsedHits
1173 
1175  bool StoreTraj(TCSlice& slc, Trajectory& tj)
1176  {
1177 
1178  if(!(tj.StepDir == 1 || tj.StepDir == -1)) {
1179  mf::LogError("TC")<<"StoreTraj: Invalid StepDir "<<tj.StepDir;
1180  return false;
1181  }
1182 
1183  if(slc.tjs.size() >= USHRT_MAX) {
1184  mf::LogError("TC")<<"StoreTraj: Too many trajectories "<<slc.tjs.size();
1185  return false;
1186  }
1187 
1188  // This shouldn't be necessary but do it anyway
1189  SetEndPoints(tj);
1190 
1191  if(tj.EndPt[1] <= tj.EndPt[0]) return false;
1192  if(tj.EndPt[1] > tj.Pts.size()) return false;
1193  unsigned short npts = tj.EndPt[1] - tj.EndPt[0] + 1;
1194  if(npts < 2) return false;
1195 
1196  auto& endTp0 = tj.Pts[tj.EndPt[0]];
1197  auto& endTp1 = tj.Pts[tj.EndPt[1]];
1198 
1199  // Calculate the charge near the end and beginning if necessary. This must be a short
1200  // trajectory. Find the average using 4 points
1201  if(endTp0.AveChg <= 0) {
1202  unsigned short cnt = 0;
1203  float sum = 0;
1204  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1205  if(tj.Pts[ipt].Chg == 0) continue;
1206  sum += tj.Pts[ipt].Chg;
1207  ++cnt;
1208  if(cnt == 4) break;
1209  }
1210  tj.Pts[tj.EndPt[0]].AveChg = sum / (float)cnt;
1211  }
1212  if(endTp1.AveChg <= 0 && npts < 5) endTp1.AveChg = endTp0.AveChg;
1213  if(endTp1.AveChg <= 0) {
1214  float sum = 0;
1215  unsigned short cnt = 0;
1216  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
1217  short ipt = tj.EndPt[1] - ii;
1218  if(ipt < 0) break;
1219  if(tj.Pts[ipt].Chg == 0) continue;
1220  sum += tj.Pts[ipt].Chg;
1221  ++cnt;
1222  if(cnt == 4) break;
1223  if(ipt == 0) break;
1224  } // ii
1225  tj.Pts[tj.EndPt[1]].AveChg = sum / (float)cnt;
1226  } // begin charge == end charge
1227 
1228 
1229  tj.DirFOM = TjDirFOM(slc, tj, false);
1230  UpdateTjChgProperties("ST", slc, tj, false);
1231 
1232  int trID = slc.tjs.size() + 1;
1233 
1234  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1235  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1236  if(tj.Pts[ipt].UseHit[ii]) {
1237  unsigned int iht = tj.Pts[ipt].Hits[ii];
1238  if(iht > slc.slHits.size() - 1) {
1239  std::cout<<"StoreTraj bad iht "<<iht<<" slHits size "<<slc.slHits.size()<<" tj.ID "<<tj.ID<<"\n";
1240  ReleaseHits(slc, tj);
1241  return false;
1242  }
1243  if(slc.slHits[iht].InTraj > 0) {
1244  std::cout<<"StoreTraj fail "<<iht<<" "<<slc.slHits[iht].InTraj<<" WorkID "<<tj.WorkID<<" InTraj "<<slc.slHits[iht].InTraj;
1245  std::cout<<" algs ";
1246  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) std::cout<<" "<<AlgBitNames[ib];
1247  std::cout<<"\n";
1248  ReleaseHits(slc, tj);
1249  return false;
1250  } // error
1251  slc.slHits[iht].InTraj = trID;
1252  }
1253  } // ii
1254  } // ipt
1255 
1256  // ensure that inTraj is clean for the ID
1257  for(unsigned int iht = 0; iht < slc.slHits.size(); ++iht) {
1258  if(slc.slHits[iht].InTraj == tj.ID) {
1259  mf::LogWarning("TC")<<"StoreTraj: Hit "<<PrintHit(slc.slHits[iht])<<" thinks it belongs to T"<<tj.ID<<" but it isn't in the Tj\n";
1260 // PrintTrajectory("ST", tjs, tj, USHRT_MAX);
1261  return false;
1262  }
1263  } // iht
1264 
1265  tj.WorkID = tj.ID;
1266  tj.ID = trID;
1267  // increment the global ID
1268  ++evt.globalTjID;
1269  tj.UID = evt.globalTjID;
1270  // Don't clobber the ParentID if it was defined by the calling function
1271  if(tj.ParentID == 0) tj.ParentID = trID;
1272  slc.tjs.push_back(tj);
1273  if(tcc.modes[kDebug] && tcc.dbgSlc && debug.Hit != UINT_MAX) {
1274  // print some debug info
1275  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
1276  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
1277  unsigned int iht = tj.Pts[ipt].Hits[ii];
1278  if(slc.slHits[iht].allHitsIndex == debug.Hit) std::cout<<"Debug hit appears in trajectory w WorkID "<<tj.WorkID<<" UseHit "<<tj.Pts[ipt].UseHit[ii]<<"\n";
1279  } // ii
1280  } // ipt
1281  } // debug.Hit ...
1282 
1283  return true;
1284 
1285  } // StoreTraj
1286 
1288  void ChgSlope(TCSlice& slc, Trajectory& tj, float& slope, float& slopeErr, float& chiDOF)
1289  {
1290  // Fits the points with charge on the Tj and returns the charge slope, slope error and
1291  // Chi/DOF
1292  ChgSlope(slc, tj, tj.EndPt[0], tj.EndPt[1], slope, slopeErr, chiDOF);
1293  } // ChgSlope
1294 
1296  void ChgSlope(TCSlice& slc, Trajectory& tj, unsigned short fromPt, unsigned short toPt, float& slope, float& slopeErr, float& chiDOF)
1297  {
1298  // Fits the points with charge on the Tj and returns the charge slope, slope error and
1299  // Chi/DOF
1300 
1301  slope = -1000;
1302  slopeErr = 1000;
1303  chiDOF = 1000;
1304 
1305  // prepare to do the fit
1306  Point2_t inPt;
1307  Vector2_t outVec, outVecErr;
1308  float chgErr;
1309  // Initialize
1310  Fit2D(0, inPt, chgErr, outVec, outVecErr, chiDOF);
1311  float wire0 = -1;
1312  unsigned short cnt = 0;
1313  for(unsigned short ipt = fromPt; ipt <= toPt; ++ipt) {
1314  auto& tp = tj.Pts[ipt];
1315  if(tp.Chg <= 0) continue;
1316  ++cnt;
1317  if(wire0 < 0) wire0 = tp.Pos[0];
1318  // Accumulate and save points
1319  inPt[0] = std::abs(tp.Pos[0] - wire0);
1320  inPt[1] = tp.Chg;
1321  // Assume 10% point-to-point charge fluctuations
1322  chgErr = 0.1 * tp.Chg;
1323  if(!Fit2D(2, inPt, chgErr, outVec, outVecErr, chiDOF)) break;
1324  } // tp
1325  if(cnt < 3) return;
1326  // do the fit and get the results
1327  if(!Fit2D(-1, inPt, chgErr, outVec, outVecErr, chiDOF)) return;
1328  slope = outVec[1];
1329  slopeErr = outVecErr[1];
1330  } // ChgSlope
1331 
1333  bool InTrajOK(TCSlice& slc, std::string someText)
1334  {
1335  // Check slc.tjs -> InTraj associations
1336 
1337  unsigned short tID;
1338  unsigned int iht;
1339  unsigned short itj = 0;
1340  std::vector<unsigned int> tHits;
1341  std::vector<unsigned int> atHits;
1342  for(auto& tj : slc.tjs) {
1343  // ignore abandoned trajectories
1344  if(tj.AlgMod[kKilled]) continue;
1345  tID = tj.ID;
1346  if(tj.AlgMod[kKilled]) {
1347  std::cout<<someText<<" ChkInTraj hit size mis-match in tj ID "<<tj.ID<<" AlgBitNames";
1348  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) std::cout<<" "<<AlgBitNames[ib];
1349  std::cout<<"\n";
1350  continue;
1351  }
1352  tHits = PutTrajHitsInVector(tj, kUsedHits);
1353  if(tHits.size() < 2) {
1354  std::cout<<someText<<" ChkInTraj: Insufficient hits in traj "<<tj.ID<<"\n";
1355  PrintTrajectory("CIT", slc, tj, USHRT_MAX);
1356  continue;
1357  }
1358  std::sort(tHits.begin(), tHits.end());
1359  atHits.clear();
1360  for(iht = 0; iht < slc.slHits.size(); ++iht) {
1361  if(slc.slHits[iht].InTraj == tID) atHits.push_back(iht);
1362  } // iht
1363  if(atHits.size() < 2) {
1364  std::cout<<someText<<" ChkInTraj: Insufficient hits in atHits in traj "<<tj.ID<<" Killing it\n";
1365  tj.AlgMod[kKilled] = true;
1366  continue;
1367  }
1368  if(!std::equal(tHits.begin(), tHits.end(), atHits.begin())) {
1369  mf::LogVerbatim myprt("TC");
1370  myprt<<someText<<" ChkInTraj failed: inTraj - UseHit mis-match for T"<<tID<<" tj.WorkID "<<tj.WorkID<<" atHits size "<<atHits.size()<<" tHits size "<<tHits.size()<<" in CTP "<<tj.CTP<<"\n";
1371  myprt<<"AlgMods: ";
1372  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
1373  myprt<<"\n";
1374  myprt<<"index inTraj UseHit \n";
1375  for(iht = 0; iht < atHits.size(); ++iht) {
1376  myprt<<"iht "<<iht<<" "<<PrintHit(slc.slHits[atHits[iht]]);
1377  if(iht < tHits.size()) myprt<<" "<<PrintHit(slc.slHits[tHits[iht]]);
1378  if(atHits[iht] != tHits[iht]) myprt<<" <<< "<<atHits[iht]<<" != "<<tHits[iht];
1379  myprt<<"\n";
1380  } // iht
1381  if(tHits.size() > atHits.size()) {
1382  for(iht = atHits.size(); iht < atHits.size(); ++iht) {
1383  myprt<<"atHits "<<iht<<" "<<PrintHit(slc.slHits[atHits[iht]])<<"\n";
1384  } // iht
1385  PrintTrajectory("CIT", slc, tj, USHRT_MAX);
1386  } // tHit.size > atHits.size()
1387  return false;
1388  }
1389  // check the VtxID
1390  for(unsigned short end = 0; end < 2; ++end) {
1391  if(tj.VtxID[end] > slc.vtxs.size()) {
1392  mf::LogVerbatim("TC")<<someText<<" ChkInTraj: Bad VtxID "<<tj.ID;
1393  std::cout<<someText<<" ChkInTraj: Bad VtxID "<<tj.ID<<" vtx size "<<slc.vtxs.size()<<"\n";
1394  tj.AlgMod[kKilled] = true;
1395  PrintTrajectory("CIT", slc, tj, USHRT_MAX);
1396  return false;
1397  }
1398  } // end
1399  ++itj;
1400  } // tj
1401  return true;
1402 
1403  } // InTrajOK
1404 
1406  void CheckTrajBeginChg(TCSlice& slc, unsigned short itj)
1407  {
1408  // This function is called after the beginning of the tj has been inspected to see if
1409  // reverse propagation was warranted. Trajectory points at the beginning were removed by
1410  // this process.
1411  // A search has been made for a Bragg peak with nothing
1412  // found. Here we look for a charge pattern like the following, where C means large charge
1413  // and c means lower charge:
1414  // CCCCCCccccccc
1415  // The charge in the two regions should be fairly uniform.
1416 
1417  // This function may split the trajectory so it needs to have been stored
1418  if(itj > slc.tjs.size() - 1) return;
1419  auto& tj = slc.tjs[itj];
1420 
1421  if(!tcc.useAlg[kBeginChg]) return;
1422  if(tj.StopFlag[0][kBragg]) return;
1423  if(tj.AlgMod[kFTBRvProp]) return;
1424  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return;
1425  if(tj.Pts.size() < 20) return;
1426 
1427  bool prt = (tcc.dbgSlc && (tcc.dbgStp || tcc.dbgAlg[kBeginChg]));
1428 
1429  // look for a large drop between the average charge near the beginning
1430  float chg2 = tj.Pts[tj.EndPt[0] + 2].AveChg;
1431  // and the average charge 15 points away
1432  float chg15 = tj.Pts[tj.EndPt[0] + 15].AveChg;
1433  if(chg2 < 3 * chg15) return;
1434 
1435  // find the point where the charge falls below the mid-point
1436  float midChg = 0.5 * (chg2 + chg15);
1437 
1438  unsigned short breakPt = USHRT_MAX;
1439  for(unsigned short ipt = tj.EndPt[0] + 3; ipt < 15; ++ipt) {
1440  float chgm2 = tj.Pts[ipt - 2].Chg;
1441  if(chgm2 == 0) continue;
1442  float chgm1 = tj.Pts[ipt - 1].Chg;
1443  if(chgm1 == 0) continue;
1444  float chgp1 = tj.Pts[ipt + 1].Chg;
1445  if(chgp1 == 0) continue;
1446  float chgp2 = tj.Pts[ipt + 2].Chg;
1447  if(chgp2 == 0) continue;
1448  if(chgm2 > midChg && chgm1 > midChg && chgp1 < midChg && chgp2 < midChg) {
1449  breakPt = ipt;
1450  break;
1451  }
1452  } // breakPt
1453  if(breakPt == USHRT_MAX) return;
1454  if(tcc.useAlg[kNewStpCuts]) {
1455  // check the charge and rms before and after the split
1456  std::array<double, 2> cnt, sum, sum2;
1457  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
1458  auto& tp = tj.Pts[ipt];
1459  if(tp.Chg <= 0) continue;
1460  unsigned short end = 0;
1461  if(ipt > breakPt) end = 1;
1462  ++cnt[end];
1463  sum[end] += tp.Chg;
1464  sum2[end] += tp.Chg * tp.Chg;
1465  } // ipt
1466  for(unsigned short end = 0; end < 2; ++end) {
1467  if(cnt[end] < 3) return;
1468  double ave = sum[end] / cnt[end];
1469  double arg = sum2[end] - cnt[end] * ave * ave;
1470  if(arg <= 0) return;
1471  sum2[end] = sqrt(arg / (cnt[end] - 1));
1472  sum2[end] /= ave;
1473  sum[end] = ave;
1474  } // region
1475  bool doSplit = true;
1476  // don't split if this looks like an electron - no significant improvement
1477  // in the charge rms before and after
1478  if(tj.ChgRMS > 0.5 && sum2[0] > 0.3 && sum2[1] > 0.3) doSplit = false;
1479  if(prt) {
1480  mf::LogVerbatim myprt("TC");
1481  myprt<<"CTBC: T"<<tj.ID<<" chgRMS "<<tj.ChgRMS;
1482  myprt<<" AveChg before split point "<<(int)sum[0]<<" rms "<<sum2[0];
1483  myprt<<" after "<<(int)sum[1]<<" rms "<<sum2[1]<<" doSplit? "<<doSplit;
1484  } // prt
1485  if(!doSplit) return;
1486  } // NewStpCuts
1487  // Create a vertex at the break point
1488  VtxStore aVtx;
1489  aVtx.Pos = tj.Pts[breakPt].Pos;
1490  aVtx.NTraj = 2;
1491  aVtx.Pass = tj.Pass;
1492  aVtx.Topo = 8;
1493  aVtx.ChiDOF = 0;
1494  aVtx.CTP = tj.CTP;
1495  aVtx.ID = slc.vtxs.size() + 1;
1496  aVtx.Stat[kFixed] = true;
1497  unsigned short ivx = slc.vtxs.size();
1498  if(!StoreVertex(slc, aVtx)) return;
1499  if(!SplitTraj(slc, itj, breakPt, ivx, prt)) {
1500  if(prt) mf::LogVerbatim("TC")<<"CTBC: Failed to split trajectory";
1501  MakeVertexObsolete("CTBC", slc, slc.vtxs[ivx], false);
1502  return;
1503  }
1504  SetVx2Score(slc);
1505 
1506  if(prt) mf::LogVerbatim("TC")<<"CTBC: Split T"<<tj.ID<<" at "<<PrintPos(slc, tj.Pts[breakPt].Pos)<<"\n";
1507 
1508  } // CheckTrajBeginChg
1509 
1511  void TrimEndPts(std::string fcnLabel, TCSlice& slc, Trajectory& tj, const std::vector<float>& fQualityCuts, bool prt)
1512  {
1513  // Trim the hits off the end until there are at least MinPts consecutive hits at the end
1514  // and the fraction of hits on the trajectory exceeds fQualityCuts[0]
1515  // Minimum length requirement accounting for dead wires where - denotes a wire with a point
1516  // and D is a dead wire. Here is an example with minPts = 3
1517  // ---DDDDD--- is OK
1518  // ----DD-DD-- is OK
1519  // ----DDD-D-- is OK
1520  // ----DDDDD-- is not OK
1521 
1522  if(!tcc.useAlg[kTEP]) return;
1523  if(tcc.useAlg[kNewStpCuts] && tj.PDGCode == 111) return;
1524 
1525  unsigned short npwc = NumPtsWithCharge(slc, tj, false);
1526  short minPts = fQualityCuts[1];
1527  if(minPts < 1) return;
1528  if(npwc < minPts) return;
1529 
1530  // handle short tjs
1531  if(npwc == minPts + 1) {
1532  unsigned short endPt1 = tj.EndPt[1];
1533  auto& tp = tj.Pts[endPt1];
1534  auto& ptp = tj.Pts[endPt1 - 1];
1535  // remove the last point if the previous point has no charge or if
1536  // it isn't on the next wire
1537  float dwire = std::abs(ptp.Pos[0] - tp.Pos[0]);
1538  if(ptp.Chg == 0 || dwire > 1.1) {
1539  UnsetUsedHits(slc, tp);
1540  SetEndPoints(tj);
1541  tj.AlgMod[kTEP] = true;
1542  }
1543  return;
1544  } // short tj
1545 
1546  // find the separation between adjacent points, starting at the end
1547  short lastPt = 0;
1548  for(lastPt = tj.EndPt[1]; lastPt >= minPts; --lastPt) {
1549  // check for an error
1550  if(lastPt == 1) break;
1551  if(tj.Pts[lastPt].Chg == 0) continue;
1552  // number of adjacent points on adjacent wires
1553  unsigned short nadj = 0;
1554  unsigned short npwc = 0;
1555  for(short ipt = lastPt - minPts; ipt < lastPt; ++ipt) {
1556  if(ipt < 2) break;
1557  // the current point
1558  auto& tp = tj.Pts[ipt];
1559  // the previous point
1560  auto& ptp = tj.Pts[ipt - 1];
1561  if(tp.Chg > 0 && ptp.Chg > 0) {
1562  ++npwc;
1563  if(std::abs(tp.Pos[0] - ptp.Pos[0]) < 1.5) ++nadj;
1564  }
1565  } // ipt
1566  float ntpwc = NumPtsWithCharge(slc, tj, true, tj.EndPt[0], lastPt);
1567  float nwires = std::abs(tj.Pts[tj.EndPt[0]].Pos[0] - tj.Pts[lastPt].Pos[0]) + 1;
1568  float hitFrac = ntpwc / nwires;
1569  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<"-TEP: T"<<tj.ID<<" lastPt "<<lastPt<<" npwc "<<npwc<<" ntpwc "<<ntpwc<<" nadj "<<nadj<<" hitFrac "<<hitFrac;
1570  if(tcc.useAlg[kNewStpCuts]) {
1571  // use new cuts
1572  if(hitFrac > fQualityCuts[0] && ntpwc > minPts) break;
1573  } else {
1574  if(hitFrac > fQualityCuts[0] && npwc == minPts && nadj == minPts) break;
1575  }
1576  } // lastPt
1577 
1578  // trim the last point if it just after a dead wire.
1579  if(tj.Pts[lastPt].Pos[0] > -0.4) {
1580  unsigned int prevWire = std::nearbyint(tj.Pts[lastPt].Pos[0]);
1581  if(tj.StepDir > 0) {
1582  --prevWire;
1583  } else {
1584  ++prevWire;
1585  }
1586  if(prt) {
1587  mf::LogVerbatim("TC")<<fcnLabel<<"-TEP: is prevWire "<<prevWire<<" dead? ";
1588  }
1589  unsigned short plane = DecodeCTP(tj.CTP).Plane;
1590  if(prevWire < slc.nWires[plane] && slc.wireHitRange[plane][prevWire].first == -1) --lastPt;
1591  } // valid Pos[0]
1592 
1593  // Nothing needs to be done
1594  if(lastPt == tj.EndPt[1]) {
1595  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<"-TEPo: Tj is OK";
1596  return;
1597  }
1598 
1599  // clear the points after lastPt
1600  for(unsigned short ipt = lastPt + 1; ipt <= tj.EndPt[1]; ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
1601  SetEndPoints(tj);
1602  tj.AlgMod[kTEP] = true;
1603  if(prt) {
1604  fcnLabel += "-TEPo";
1605  PrintTrajectory(fcnLabel, slc, tj, USHRT_MAX);
1606  }
1607 
1608  } // TrimEndPts
1609 
1611  void ChkChgAsymmetry(TCSlice& slc, Trajectory& tj, bool prt)
1612  {
1613  // looks for a high-charge point in the trajectory which may be due to the
1614  // trajectory crossing an interaction vertex. The properties of points on the opposite
1615  // sides of the high-charge point are analyzed. If significant differences are found, all points
1616  // near the high-charge point are removed as well as those from that point to the end
1617  if(!tcc.useAlg[kChkChgAsym]) return;
1618  if(tcc.useAlg[kNewStpCuts] && tj.PDGCode == 111) return;
1619  unsigned short npts = tj.EndPt[1] - tj.EndPt[0];
1620  if(prt) mf::LogVerbatim("TC")<<" Inside ChkChgAsymmetry T"<<tj.ID;
1621  // ignore long tjs
1622  if(npts > 50) return;
1623  // ignore short tjs
1624  if(npts < 8) return;
1625  // require the charge pull > 5
1626  float bigPull = 5;
1627  unsigned short atPt = 0;
1628  // Don't consider the first/last few points in case there is a Bragg peak
1629  for(unsigned short ipt = tj.EndPt[0] + 2; ipt <= tj.EndPt[1] - 2; ++ipt) {
1630  auto& tp = tj.Pts[ipt];
1631  if(tp.ChgPull > bigPull) {
1632  bigPull = tp.ChgPull;
1633  atPt = ipt;
1634  }
1635  } // ipt
1636  if(atPt == 0) return;
1637  // require that this point be near the DS end
1638  if((atPt - tj.EndPt[0]) < 0.5 * npts) return;
1639  if(prt) mf::LogVerbatim("TC")<<"CCA: T"<<tj.ID<<" Large Chg point at "<<atPt<<". Check charge asymmetry around it.";
1640  unsigned short nchk = 0;
1641  unsigned short npos = 0;
1642  unsigned short nneg = 0;
1643  for(short ii = 1; ii < 5; ++ii) {
1644  short iplu = atPt + ii;
1645  if(iplu > tj.EndPt[1]) break;
1646  short ineg = atPt - ii;
1647  if(ineg < tj.EndPt[0]) break;
1648  if(tj.Pts[iplu].Chg == 0) continue;
1649  if(tj.Pts[ineg].Chg == 0) continue;
1650  float asym = (tj.Pts[iplu].Chg - tj.Pts[ineg].Chg) / (tj.Pts[iplu].Chg + tj.Pts[ineg].Chg);
1651  ++nchk;
1652  if(asym > 0.5) ++npos;
1653  if(asym < -0.5) ++nneg;
1654  if(prt) mf::LogVerbatim("TC")<<" ineg "<<ineg<<" iplu "<<iplu<<" asym "<<asym<<" nchk "<<nchk;
1655  } // ii
1656  if(nchk < 3) return;
1657  // require most of the points be very positive or very negative
1658  nchk -= 2;
1659  bool doTrim = (nneg > nchk) || (npos > nchk);
1660  if(!doTrim) return;
1661  // remove all the points at the end starting at the one just before the peak if the pull is not so good
1662  auto& prevTP = tj.Pts[atPt - 1];
1663  if(std::abs(prevTP.ChgPull) > 2) --atPt;
1664  for(unsigned short ipt = atPt; ipt <= tj.EndPt[1]; ++ipt) UnsetUsedHits(slc, tj.Pts[ipt]);
1665  SetEndPoints(tj);
1666  tj.AlgMod[kChkChgAsym] = true;
1667  if(prt) PrintTrajectory("CCA", slc, tj, USHRT_MAX);
1668  } // ChkChgAsymmetry
1669 
1671  bool SignalBetween(TCSlice& slc, const TrajPoint& tp1, const TrajPoint& tp2, const float& MinWireSignalFraction)
1672  {
1673  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp1 and tp2.
1674  if(MinWireSignalFraction == 0) return true;
1675 
1676  if(tp1.Pos[0] < -0.4 || tp2.Pos[0] < -0.4) return false;
1677  int fromWire = std::nearbyint(tp1.Pos[0]);
1678  int toWire = std::nearbyint(tp2.Pos[0]);
1679 
1680  if(fromWire == toWire) {
1681  TrajPoint tp = tp1;
1682  // check for a signal midway between
1683  tp.Pos[1] = 0.5 * (tp1.Pos[1] + tp2.Pos[1]);
1684  return SignalAtTp(slc, tp);
1685  }
1686  // define a trajectory point located at tp1 that has a direction towards tp2
1687  TrajPoint tp;
1688  if(!MakeBareTrajPoint(slc, tp1, tp2, tp)) return true;
1689  return SignalBetween(slc, tp, toWire, MinWireSignalFraction);
1690  } // SignalBetween
1691 
1692 
1693 
1695  bool SignalBetween(TCSlice& slc, TrajPoint tp, float toPos0, const float& MinWireSignalFraction)
1696  {
1697  // Returns true if there is a signal on > MinWireSignalFraction of the wires between tp and toPos0.
1698  return ChgFracBetween(slc, tp, toPos0) >= MinWireSignalFraction;
1699  } // SignalBetween
1700 
1702  float ChgFracBetween(TCSlice& slc, TrajPoint tp, float toPos0)
1703  {
1704  // Returns the fraction of wires between tp.Pos[0] and toPos0 that have a hit
1705  // on the line defined by tp.Pos and tp.Dir
1706 
1707  if(tp.Pos[0] < -0.4 || toPos0 < -0.4) return 0;
1708  int fromWire = std::nearbyint(tp.Pos[0]);
1709  int toWire = std::nearbyint(toPos0);
1710 
1711  if(fromWire == toWire) {
1712  return SignalAtTp(slc, tp);
1713  }
1714 
1715  int nWires = abs(toWire - fromWire) + 1;
1716 
1717  if(std::abs(tp.Dir[0]) < 0.001) tp.Dir[0] = 0.001;
1718  float stepSize = std::abs(1/tp.Dir[0]);
1719  // ensure that we step in the right direction
1720  if(toWire > fromWire && tp.Dir[0] < 0) stepSize = -stepSize;
1721  if(toWire < fromWire && tp.Dir[0] > 0) stepSize = -stepSize;
1722  float nsig = 0;
1723  float num = 0;
1724  for(unsigned short cnt = 0; cnt < nWires; ++cnt) {
1725  ++num;
1726  if(SignalAtTp(slc, tp)) ++nsig;
1727  tp.Pos[0] += tp.Dir[0] * stepSize;
1728  tp.Pos[1] += tp.Dir[1] * stepSize;
1729  } // cnt
1730  float sigFrac = nsig / num;
1731  return sigFrac;
1732 
1733  } // ChgFracBetween
1734 
1736  bool TrajHitsOK(TCSlice& slc, const std::vector<unsigned int>& iHitsInMultiplet, const std::vector<unsigned int>& jHitsInMultiplet)
1737  {
1738  // Hits (assume to be on adjacent wires have an acceptable signal overlap
1739 
1740  if(iHitsInMultiplet.empty() || jHitsInMultiplet.empty()) return false;
1741 
1742  float sum;
1743  float cvI = HitsPosTick(slc, iHitsInMultiplet, sum, kAllHits);
1744  float minI = 1E6;
1745  float maxI = 0;
1746  for(auto& iht : iHitsInMultiplet) {
1747  auto const& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1748  float cv = hit.PeakTime();
1749  float rms = hit.RMS();
1750  float arg = cv - 3.1 * rms;
1751  if(arg < minI) minI = arg;
1752  arg = cv + 3.1 * rms;
1753  if(arg > maxI) maxI = arg;
1754  }
1755 
1756  float cvJ = HitsPosTick(slc, jHitsInMultiplet, sum, kAllHits);
1757  float minJ = 1E6;
1758  float maxJ = 0;
1759  for(auto& jht : jHitsInMultiplet) {
1760  auto& hit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
1761  float cv = hit.PeakTime();
1762  float rms = hit.RMS();
1763  float arg = cv - 3.1 * rms;
1764  if(arg < minJ) minJ = arg;
1765  arg = cv + 3.1 * rms;
1766  if(arg > maxJ) maxJ = arg;
1767  }
1768 
1769  if(cvI < cvJ) {
1770  if(maxI > minJ) return true;
1771  } else {
1772  if(minI < maxJ) return true;
1773  }
1774  return false;
1775  } // TrajHitsOK
1776 
1778  bool TrajHitsOK(TCSlice& slc, const unsigned int iht, const unsigned int jht)
1779  {
1780  // ensure that two adjacent hits have an acceptable overlap
1781  if(iht > slc.slHits.size() - 1) return false;
1782  if(jht > slc.slHits.size() - 1) return false;
1783  // require that they be on adjacent wires
1784  auto& ihit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1785  auto& jhit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
1786  int iwire = ihit.WireID().Wire;
1787  int jwire = jhit.WireID().Wire;
1788  if(std::abs(iwire - jwire) > 1) return false;
1789  if(ihit.PeakTime() > jhit.PeakTime()) {
1790  float minISignal = ihit.PeakTime() - 3 * ihit.RMS();
1791  float maxJSignal = jhit.PeakTime() + 3 * ihit.RMS();
1792  if(maxJSignal > minISignal) return true;
1793  } else {
1794  float maxISignal = ihit.PeakTime() + 3 * ihit.RMS();
1795  float minJSignal = jhit.PeakTime() - 3 * ihit.RMS();
1796  if(minJSignal > maxISignal) return true;
1797  }
1798  return false;
1799  } // TrajHitsOK
1800 
1802  float ExpectedHitsRMS(TCSlice& slc, const TrajPoint& tp)
1803  {
1804  // returns the expected RMS of hits for the trajectory point in ticks
1805  if(std::abs(tp.Dir[0]) > 0.001) {
1806  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1807  return 1.5 * evt.aveHitRMS[planeID.Plane] + 2 * std::abs(tp.Dir[1]/tp.Dir[0])/tcc.unitsPerTick;
1808  } else {
1809  return 500;
1810  }
1811  } // ExpectedHitsRMS
1812 
1814  bool SignalAtTp(TCSlice& slc, const TrajPoint& tp)
1815  {
1816  // returns true if there is a hit near tp.Pos
1817 
1818  if(tp.Pos[0] < -0.4) return false;
1819  unsigned int wire = std::nearbyint(tp.Pos[0]);
1820  geo::PlaneID planeID = DecodeCTP(tp.CTP);
1821  unsigned int ipl = planeID.Plane;
1822  if(wire >= slc.nWires[ipl]) return false;
1823  if(tp.Pos[1] > tcc.maxPos1[ipl]) return false;
1824  // Assume dead wires have a signal
1825  if(slc.wireHitRange[ipl][wire].first == -1) return true;
1826  float projTick = (float)(tp.Pos[1] / tcc.unitsPerTick);
1827  float tickRange = 0;
1828  if(std::abs(tp.Dir[1]) != 0) {
1829  tickRange = std::abs(0.5 / tp.Dir[1]) / tcc.unitsPerTick;
1830  // don't let it get too large
1831  if(tickRange > 40) tickRange = 40;
1832  }
1833  float loTpTick = projTick - tickRange;
1834  float hiTpTick = projTick + tickRange;
1835  unsigned int firstHit = (unsigned int)slc.wireHitRange[ipl][wire].first;
1836  unsigned int lastHit = (unsigned int)slc.wireHitRange[ipl][wire].second;
1837 
1838  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
1839  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
1840  if(projTick < hit.PeakTime()) {
1841  float loHitTick = hit.PeakTime() - 3 * hit.RMS();
1842  if(hiTpTick > loHitTick) return true;
1843  } else {
1844  float hiHitTick = hit.PeakTime() + 3 * hit.RMS();
1845  if(loTpTick < hiHitTick) return true;
1846  }
1847  } // iht
1848  return false;
1849 
1850  } // SignalAtTp
1851 
1853  float TpSumHitChg(TCSlice& slc, TrajPoint const& tp){
1854  float totchg = 0;
1855  for (size_t i = 0; i<tp.Hits.size(); ++i){
1856  if (!tp.UseHit[i]) continue;
1857  totchg += (*evt.allHits)[slc.slHits[tp.Hits[i]].allHitsIndex].Integral();
1858  }
1859  return totchg;
1860  } // TpSumHitChg
1861 
1863  unsigned short NumPtsWithCharge(TCSlice& slc, const Trajectory& tj, bool includeDeadWires)
1864  {
1865  unsigned short firstPt = tj.EndPt[0];
1866  unsigned short lastPt = tj.EndPt[1];
1867  return NumPtsWithCharge(slc, tj, includeDeadWires, firstPt, lastPt);
1868  }
1869 
1871  unsigned short NumPtsWithCharge(TCSlice& slc, const Trajectory& tj, bool includeDeadWires, unsigned short firstPt, unsigned short lastPt)
1872  {
1873  unsigned short ntp = 0;
1874  for(unsigned short ipt = firstPt; ipt <= lastPt; ++ipt) if(tj.Pts[ipt].Chg > 0) ++ntp;
1875  // Add the count of deadwires
1876  if(includeDeadWires) ntp += DeadWireCount(slc, tj.Pts[firstPt], tj.Pts[lastPt]);
1877  return ntp;
1878  } // NumPtsWithCharge
1879 
1881  float DeadWireCount(TCSlice& slc, const TrajPoint& tp1, const TrajPoint& tp2)
1882  {
1883  return DeadWireCount(slc, tp1.Pos[0], tp2.Pos[0], tp1.CTP);
1884  } // DeadWireCount
1885 
1887  float DeadWireCount(TCSlice& slc, const float& inWirePos1, const float& inWirePos2, CTP_t tCTP)
1888  {
1889  if(inWirePos1 < -0.4 || inWirePos2 < -0.4) return 0;
1890  unsigned int inWire1 = std::nearbyint(inWirePos1);
1891  unsigned int inWire2 = std::nearbyint(inWirePos2);
1892  geo::PlaneID planeID = DecodeCTP(tCTP);
1893  unsigned short plane = planeID.Plane;
1894  if(inWire1 > slc.nWires[plane] || inWire2 > slc.nWires[plane]) return 0;
1895  if(inWire1 > inWire2) {
1896  // put in increasing order
1897  unsigned int tmp = inWire1;
1898  inWire1 = inWire2;
1899  inWire2 = tmp;
1900  } // inWire1 > inWire2
1901  ++inWire2;
1902  unsigned int wire, ndead = 0;
1903  for(wire = inWire1; wire < inWire2; ++wire) if(slc.wireHitRange[plane][wire].first == -1) ++ndead;
1904  return ndead;
1905  } // DeadWireCount
1906 
1908  unsigned short PDGCodeIndex(int PDGCode)
1909  {
1910  unsigned short pdg = abs(PDGCode);
1911  if(pdg == 11) return 0; // electron
1912  if(pdg == 13) return 1; // muon
1913  if(pdg == 211) return 2; // pion
1914  if(pdg == 321) return 3; // kaon
1915  if(pdg == 2212) return 4; // proton
1916 
1917  return USHRT_MAX;
1918 
1919  } // PDGCodeIndex
1920 
1922  void MakeTrajectoryObsolete(TCSlice& slc, unsigned int itj)
1923  {
1924  // Note that this does not change the state of UseHit to allow
1925  // resurrecting the trajectory later (RestoreObsoleteTrajectory)
1926  if(itj > slc.tjs.size() - 1) return;
1927  int killTjID = slc.tjs[itj].ID;
1928  for(auto& hit : slc.slHits) if(hit.InTraj == killTjID) hit.InTraj = 0;
1929  slc.tjs[itj].AlgMod[kKilled] = true;
1930  } // MakeTrajectoryObsolete
1931 
1933  void RestoreObsoleteTrajectory(TCSlice& slc, unsigned int itj)
1934  {
1935  if(itj > slc.tjs.size() - 1) return;
1936  if(!slc.tjs[itj].AlgMod[kKilled]) {
1937  mf::LogWarning("TC")<<"RestoreObsoleteTrajectory: Trying to restore not-obsolete trajectory "<<slc.tjs[itj].ID;
1938  return;
1939  }
1940  unsigned int iht;
1941  for(auto& tp : slc.tjs[itj].Pts) {
1942  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
1943  if(tp.UseHit[ii]) {
1944  iht = tp.Hits[ii];
1945  if(slc.slHits[iht].InTraj == 0) {
1946  slc.slHits[iht].InTraj = slc.tjs[itj].ID;
1947  }
1948  }
1949  } // ii
1950  } // tp
1951  slc.tjs[itj].AlgMod[kKilled] = false;
1952  } // RestoreObsoleteTrajectory
1953 
1955  void MergeGhostTjs(TCSlice& slc, CTP_t inCTP)
1956  {
1957  // Merges short Tjs that share many hits with a longer Tj
1958  if(!tcc.useAlg[kMrgGhost]) return;
1959 
1960  for(auto& shortTj : slc.tjs) {
1961  if(shortTj.AlgMod[kKilled] || shortTj.AlgMod[kHaloTj]) continue;
1962  if(shortTj.CTP != inCTP) continue;
1963  unsigned short spts = shortTj.EndPt[1] - shortTj.EndPt[0];
1964  if(spts > 20) continue;
1965  // ignore delta rays
1966  if(shortTj.PDGCode == 11) continue;
1967  // ignore InShower Tjs
1968  if(shortTj.SSID > 0) continue;
1969  auto tjhits = PutTrajHitsInVector(shortTj, kAllHits);
1970  if(tjhits.empty()) continue;
1971  std::vector<int> tids;
1972  std::vector<unsigned short> tcnt;
1973  for(auto iht : tjhits) {
1974  auto& hit = slc.slHits[iht];
1975  if(hit.InTraj <= 0) continue;
1976  if((unsigned int)hit.InTraj > slc.tjs.size()) continue;
1977  if(hit.InTraj == shortTj.ID) continue;
1978  unsigned short indx = 0;
1979  for(indx = 0; indx < tids.size(); ++indx) if(hit.InTraj == tids[indx]) break;
1980  if(indx == tids.size()) {
1981  tids.push_back(hit.InTraj);
1982  tcnt.push_back(1);
1983  } else {
1984  ++tcnt[indx];
1985  }
1986  } // iht
1987  if(tids.empty()) continue;
1988  // find the max count for Tjs that are longer than this one
1989  unsigned short maxcnt = 0;
1990  for(unsigned short indx = 0; indx < tids.size(); ++indx) {
1991  if(tcnt[indx] > maxcnt) {
1992  auto& ltj = slc.tjs[tids[indx] - 1];
1993  unsigned short lpts = ltj.EndPt[1] - ltj.EndPt[0];
1994  if(lpts < spts) continue;
1995  maxcnt = tcnt[indx];
1996  }
1997  } // indx
1998  float hitFrac = (float)maxcnt / (float)tjhits.size();
1999  if(hitFrac < 0.1) continue;
2000  } // shortTj
2001  } // MergeGhostTjs
2002 
2004  bool SplitTraj(TCSlice& slc, unsigned short itj, float XPos, bool makeVx2, bool prt)
2005  {
2006  // Splits the trajectory at an X position and optionally creates a 2D vertex at the split point
2007  if(itj > slc.tjs.size()-1) return false;
2008 
2009  auto& tj = slc.tjs[itj];
2010  geo::PlaneID planeID = DecodeCTP(tj.CTP);
2011  float atPos1 = tcc.detprop->ConvertXToTicks(XPos, planeID) * tcc.unitsPerTick;
2012  unsigned short atPt = USHRT_MAX;
2013  for(unsigned short ipt = tj.EndPt[0] + 1; ipt <= tj.EndPt[1]; ++ipt) {
2014  if(tj.Pts[ipt].Pos[1] > tj.Pts[ipt - 1].Pos[1]) {
2015  // positive slope
2016  if(tj.Pts[ipt - 1].Pos[1] < atPos1 && tj.Pts[ipt].Pos[1] >= atPos1) {
2017  atPt = ipt;
2018  break;
2019  }
2020  } else {
2021  // negative slope
2022  if(tj.Pts[ipt - 1].Pos[1] >= atPos1 && tj.Pts[ipt].Pos[1] < atPos1) {
2023  atPt = ipt;
2024  break;
2025  }
2026  } // negative slope
2027  } // ipt
2028  if(atPt == USHRT_MAX) return false;
2029  unsigned short vx2Index = USHRT_MAX;
2030  if(makeVx2) {
2031  VtxStore newVx2;
2032  newVx2.CTP = tj.CTP;
2033  newVx2.Pos[0] = 0.5 * (tj.Pts[atPt - 1].Pos[0] + tj.Pts[atPt].Pos[0]);
2034  newVx2.Pos[1] = 0.5 * (tj.Pts[atPt - 1].Pos[1] + tj.Pts[atPt].Pos[1]);
2035  newVx2.Topo = 10;
2036  newVx2.NTraj = 2;
2037  if(StoreVertex(slc, newVx2)) vx2Index = slc.vtxs.size() - 1;
2038  } // makeVx2
2039  return SplitTraj(slc, itj, atPt, vx2Index, prt);
2040  } // SplitTraj
2041 
2043  bool SplitTraj(TCSlice& slc, unsigned short itj, unsigned short pos, unsigned short ivx, bool prt)
2044  {
2045  // Splits the trajectory itj in the slc.tjs vector into two trajectories at position pos. Splits
2046  // the trajectory and associates the ends to the supplied vertex.
2047  // Here is an example where itj has 9 points and we will split at pos = 4
2048  // itj (0 1 2 3 4 5 6 7 8) -> new traj (0 1 2 3) + new traj (4 5 6 7 8)
2049 
2050  if(itj > slc.tjs.size()-1) return false;
2051  if(pos < slc.tjs[itj].EndPt[0] + 1 || pos > slc.tjs[itj].EndPt[1] - 1) return false;
2052  if(ivx != USHRT_MAX && ivx > slc.vtxs.size() - 1) return false;
2053 
2054  Trajectory& tj = slc.tjs[itj];
2055 
2056  // Reset the PDG Code if we are splitting a tagged muon
2057  bool splittingMuon = (tj.PDGCode == 13);
2058  if(splittingMuon) tj.PDGCode = 0;
2059 
2060  if(prt) {
2061  mf::LogVerbatim myprt("TC");
2062  myprt<<"SplitTraj: Split T"<<tj.ID<<" at point "<<pos;
2063  if(ivx < slc.vtxs.size()) myprt<<" with Vtx 2V"<<slc.vtxs[ivx].ID;
2064  }
2065 
2066  // ensure that there will be at least 3 TPs on each trajectory
2067  unsigned short ipt, ii, ntp = 0;
2068  for(ipt = 0; ipt < pos; ++ipt) {
2069  if(tj.Pts[ipt].Chg > 0) ++ntp;
2070  if(ntp > 2) break;
2071  } // ipt
2072  if(ntp < 3) {
2073  if(prt) mf::LogVerbatim("TC")<<" Split point to small at begin "<<ntp<<" pos "<<pos<<" ID ";
2074  return false;
2075  }
2076  ntp = 0;
2077  for(ipt = pos + 1; ipt < tj.Pts.size(); ++ipt) {
2078  if(tj.Pts[ipt].Chg > 0) ++ntp;
2079  if(ntp > 2) break;
2080  } // ipt
2081  if(ntp < 3) {
2082  if(prt) mf::LogVerbatim("TC")<<" Split point too small at end "<<ntp<<" pos "<<pos<<" EndPt "<<tj.EndPt[1];
2083  return false;
2084  }
2085 
2086  // make a copy that will become the Tj after the split point
2087  Trajectory newTj = tj;
2088  newTj.ID = slc.tjs.size() + 1;
2089  ++evt.globalTjID;
2090  newTj.UID = evt.globalTjID;
2091  // make another copy in case something goes wrong
2092  Trajectory oldTj = tj;
2093 
2094  // Leave the first section of tj in place. Re-assign the hits
2095  // to the new trajectory
2096  unsigned int iht;
2097  for(ipt = pos + 1; ipt < tj.Pts.size(); ++ipt) {
2098  tj.Pts[ipt].Chg = 0;
2099  for(ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2100  if(!tj.Pts[ipt].UseHit[ii]) continue;
2101  iht = tj.Pts[ipt].Hits[ii];
2102  // This shouldn't happen but check anyway
2103  if(slc.slHits[iht].InTraj != tj.ID) continue;
2104  slc.slHits[iht].InTraj = newTj.ID;
2105  tj.Pts[ipt].UseHit[ii] = false;
2106  } // ii
2107  } // ipt
2108  SetEndPoints(tj);
2109  tj.MCSMom = MCSMom(slc, tj);
2110  UpdateTjChgProperties("ST", slc, tj, prt);
2111  if(splittingMuon) SetPDGCode(slc, tj, true);
2112 
2113  // Append 3 points from the end of tj onto the
2114  // beginning of newTj so that hits can be swapped between
2115  // them later
2116  unsigned short eraseSize = pos - 2;
2117  if(eraseSize > newTj.Pts.size() - 1) {
2118  tj = oldTj;
2119  return false;
2120  }
2121 
2122  if(ivx < slc.vtxs.size()) tj.VtxID[1] = slc.vtxs[ivx].ID;
2123  tj.AlgMod[kSplit] = true;
2124  if(prt) {
2125  mf::LogVerbatim("TC")<<" Splitting T"<<tj.ID<<" new EndPts "<<tj.EndPt[0]<<" to "<<tj.EndPt[1];
2126  }
2127 
2128  // erase the TPs at the beginning of the new trajectory
2129  newTj.Pts.erase(newTj.Pts.begin(), newTj.Pts.begin() + eraseSize);
2130  // unset the first 3 TP hits
2131  for(ipt = 0; ipt < 3; ++ipt) {
2132  for(ii = 0; ii < newTj.Pts[ipt].Hits.size(); ++ii) newTj.Pts[ipt].UseHit[ii] = false;
2133  newTj.Pts[ipt].Chg = 0;
2134  } // ipt
2135  SetEndPoints(newTj);
2136  newTj.MCSMom = MCSMom(slc, newTj);
2137  UpdateTjChgProperties("ST", slc, newTj, prt);
2138  if(splittingMuon) SetPDGCode(slc, newTj, true);
2139  if(ivx < slc.vtxs.size()) newTj.VtxID[0] = slc.vtxs[ivx].ID;
2140  newTj.AlgMod[kSplit] = true;
2141  newTj.ParentID = 0;
2142  // save the ID before push_back in case the tj reference gets lost
2143  int tjid = tj.ID;
2144  slc.tjs.push_back(newTj);
2145  UpdateMatchStructs(slc, tjid, newTj.ID);
2146 
2147  if(prt) {
2148  mf::LogVerbatim("TC")<<" newTj T"<<newTj.ID<<" EndPts "<<newTj.EndPt[0]<<" to "<<newTj.EndPt[1];
2149  }
2150  return true;
2151 
2152  } // SplitTraj
2153 
2155  void TrajPointTrajDOCA(TCSlice& slc, TrajPoint const& tp, Trajectory const& tj, unsigned short& closePt, float& minSep)
2156  {
2157  // Finds the point, ipt, on trajectory tj that is closest to trajpoint tp
2158  float best = minSep * minSep;
2159  closePt = USHRT_MAX;
2160  float dw, dt, dp2;
2161  unsigned short ipt;
2162  for(ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2163  dw = tj.Pts[ipt].Pos[0] - tp.Pos[0];
2164  dt = tj.Pts[ipt].Pos[1] - tp.Pos[1];
2165  dp2 = dw * dw + dt * dt;
2166  if(dp2 < best) {
2167  best = dp2;
2168  closePt = ipt;
2169  }
2170  } // ipt
2171  minSep = sqrt(best);
2172  } // TrajPointTrajDOCA
2173 
2175  bool TrajTrajDOCA(TCSlice& slc, const Trajectory& tj1, const Trajectory& tj2, unsigned short& ipt1, unsigned short& ipt2, float& minSep)
2176  {
2177  return TrajTrajDOCA(slc, tj1, tj2, ipt1, ipt2, minSep, false);
2178  } // TrajTrajDOCA
2179 
2181  bool TrajTrajDOCA(TCSlice& slc, const Trajectory& tj1, const Trajectory& tj2, unsigned short& ipt1, unsigned short& ipt2, float& minSep, bool considerDeadWires)
2182  {
2183  // Find the Distance Of Closest Approach between two trajectories less than minSep
2184  // start with some rough cuts to minimize the use of the more expensive checking. This
2185  // function returns true if the DOCA is less than minSep
2186  for(unsigned short iwt = 0; iwt < 2; ++iwt) {
2187  // Apply box cuts on the ends of the trajectories
2188  // The Lo/Hi wire(time) at each end of tj1
2189  float wt0 = tj1.Pts[tj1.EndPt[0]].Pos[iwt];
2190  float wt1 = tj1.Pts[tj1.EndPt[1]].Pos[iwt];
2191  float lowt1 = wt0;
2192  float hiwt1 = wt1;
2193  if(wt1 < lowt1) {
2194  lowt1 = wt1;
2195  hiwt1 = wt0;
2196  }
2197  // The Lo/Hi wire(time) at each end of tj2
2198  wt0 = tj2.Pts[tj2.EndPt[0]].Pos[iwt];
2199  wt1 = tj2.Pts[tj2.EndPt[1]].Pos[iwt];
2200  float lowt2 = wt0;
2201  float hiwt2 = wt1;
2202  if(wt1 < lowt2) {
2203  lowt2 = wt1;
2204  hiwt2 = wt0;
2205  }
2206  // Check for this configuration
2207  // loWire1.......hiWire1 minSep loWire2....hiWire2
2208  // loTime1.......hiTime1 minSep loTime2....hiTime2
2209  if(lowt2 > hiwt1 + minSep) return false;
2210  // and the other
2211  if(lowt1 > hiwt2 + minSep) return false;
2212  } // iwt
2213 
2214  float best = minSep * minSep;
2215  ipt1 = 0; ipt2 = 0;
2216  float dwc = 0;
2217  bool isClose = false;
2218  for(unsigned short i1 = tj1.EndPt[0]; i1 < tj1.EndPt[1] + 1; ++i1) {
2219  for(unsigned short i2 = tj2.EndPt[0]; i2 < tj2.EndPt[1] + 1; ++i2) {
2220  if(considerDeadWires) dwc = DeadWireCount(slc, tj1.Pts[i1], tj2.Pts[i2]);
2221  float dw = tj1.Pts[i1].Pos[0] - tj2.Pts[i2].Pos[0] - dwc;
2222  if(std::abs(dw) > minSep) continue;
2223  float dt = tj1.Pts[i1].Pos[1] - tj2.Pts[i2].Pos[1];
2224  if(std::abs(dt) > minSep) continue;
2225  float dp2 = dw * dw + dt * dt;
2226  if(dp2 < best) {
2227  best = dp2;
2228  ipt1 = i1;
2229  ipt2 = i2;
2230  isClose = true;
2231  }
2232  } // i2
2233  } // i1
2234  minSep = sqrt(best);
2235  return isClose;
2236  } // TrajTrajDOCA
2237 
2239  float HitSep2(TCSlice& slc, unsigned int iht, unsigned int jht)
2240  {
2241  // returns the separation^2 between two hits in WSE units
2242  if(iht > slc.slHits.size()-1 || jht > slc.slHits.size()-1) return 1E6;
2243  auto& ihit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2244  auto& jhit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
2245  float dw = (float)ihit.WireID().Wire - (float)jhit.WireID().Wire;
2246  float dt = (ihit.PeakTime() - jhit.PeakTime()) * tcc.unitsPerTick;
2247  return dw * dw + dt * dt;
2248  } // HitSep2
2249 
2251  unsigned short CloseEnd(TCSlice& slc, const Trajectory& tj, const Point2_t& pos)
2252  {
2253  unsigned short endPt = tj.EndPt[0];
2254  auto& tp0 = tj.Pts[endPt];
2255  endPt = tj.EndPt[1];
2256  auto& tp1 = tj.Pts[endPt];
2257  if(PosSep2(tp0.Pos, pos) < PosSep2(tp1.Pos, pos)) return 0;
2258  return 1;
2259  } // CloseEnd
2260 
2262  float PointTrajSep2(float wire, float time, TrajPoint const& tp)
2263  {
2264  float dw = wire - tp.Pos[0];
2265  float dt = time - tp.Pos[1];
2266  return dw * dw + dt * dt;
2267  }
2268 
2270  float PointTrajDOCA(TCSlice& slc, unsigned int iht, TrajPoint const& tp)
2271  {
2272  if(iht > slc.slHits.size() - 1) return 1E6;
2273  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2274  float wire = hit.WireID().Wire;
2275  float time = hit.PeakTime() * tcc.unitsPerTick;
2276  return sqrt(PointTrajDOCA2(slc, wire, time, tp));
2277  } // PointTrajDOCA
2278 
2280  float PointTrajDOCA(TCSlice& slc, float wire, float time, TrajPoint const& tp)
2281  {
2282  return sqrt(PointTrajDOCA2(slc, wire, time, tp));
2283  } // PointTrajDOCA
2284 
2286  float PointTrajDOCA2(TCSlice& slc, float wire, float time, TrajPoint const& tp)
2287  {
2288  // returns the distance of closest approach squared between a (wire, time(WSE)) point
2289  // and a trajectory point
2290 
2291  double t = (double)(wire - tp.Pos[0]) * tp.Dir[0] + (double)(time - tp.Pos[1]) * tp.Dir[1];
2292  double dw = tp.Pos[0] + t * tp.Dir[0] - wire;
2293  double dt = tp.Pos[1] + t * tp.Dir[1] - time;
2294  return (float)(dw * dw + dt * dt);
2295 
2296  } // PointTrajDOCA2
2297 
2299  void TrajIntersection(TrajPoint const& tp1, TrajPoint const& tp2, Point2_t& pos)
2300  {
2301  TrajIntersection(tp1, tp2, pos[0], pos[1]);
2302  } // TrajIntersection
2304  void TrajIntersection(TrajPoint const& tp1, TrajPoint const& tp2, float& x, float& y)
2305  {
2306  // returns the intersection position, (x,y), of two trajectory points
2307 
2308  x = -9999; y = -9999;
2309 
2310  double arg1 = tp1.Pos[0] * tp1.Dir[1] - tp1.Pos[1] * tp1.Dir[0];
2311  double arg2 = tp2.Pos[0] * tp1.Dir[1] - tp2.Pos[1] * tp1.Dir[0];
2312  double arg3 = tp2.Dir[0] * tp1.Dir[1] - tp2.Dir[1] * tp1.Dir[0];
2313  if(arg3 == 0) return;
2314  double s = (arg1 - arg2) / arg3;
2315 
2316  x = (float)(tp2.Pos[0] + s * tp2.Dir[0]);
2317  y = (float)(tp2.Pos[1] + s * tp2.Dir[1]);
2318 
2319  } // TrajIntersection
2320 
2322  float MaxTjLen(TCSlice& slc, std::vector<int>& tjIDs)
2323  {
2324  // returns the length of the longest Tj in the supplied list
2325  if(tjIDs.empty()) return 0;
2326  float maxLen = 0;
2327  for(auto tjid : tjIDs) {
2328  if(tjid < 1 || tjid > (int)slc.tjs.size()) continue;
2329  auto& tj = slc.tjs[tjid - 1];
2330  float sep2 = PosSep2(tj.Pts[tj.EndPt[0]].Pos, tj.Pts[tj.EndPt[1]].Pos);
2331  if(sep2 > maxLen) maxLen = sep2;
2332  } // tj
2333  return sqrt(maxLen);
2334  } // MaxTjLen
2335 
2338  {
2339  float len = 0, dx, dy;
2340  unsigned short ipt;
2341  unsigned short prevPt = tj.EndPt[0];
2342  for(ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1] + 1; ++ipt) {
2343  if(tj.Pts[ipt].Chg == 0) continue;
2344  dx = tj.Pts[ipt].Pos[0] - tj.Pts[prevPt].Pos[0];
2345  dy = tj.Pts[ipt].Pos[1] - tj.Pts[prevPt].Pos[1];
2346  len += sqrt(dx * dx + dy * dy);
2347  prevPt = ipt;
2348  }
2349  return len;
2350  } // TrajLength
2351 
2353  float PosSep(const Point2_t& pos1, const Point2_t& pos2)
2354  {
2355  return sqrt(PosSep2(pos1, pos2));
2356  } // PosSep
2357 
2359  float PosSep2(const Point2_t& pos1, const Point2_t& pos2)
2360  {
2361  // returns the separation distance^2 between two positions
2362  float d0 = pos1[0] - pos2[0];
2363  float d1 = pos1[1] - pos2[1];
2364  return d0*d0+d1*d1;
2365  } // PosSep2
2366 
2369  {
2370  // Returns the separation distance between two trajectory points
2371  float dx = tp1.Pos[0] - tp2.Pos[0];
2372  float dy = tp1.Pos[1] - tp2.Pos[1];
2373  return sqrt(dx * dx + dy * dy);
2374  } // TrajPointSeparation
2375 
2377  bool TrajClosestApproach(Trajectory const& tj, float x, float y, unsigned short& closePt, float& DOCA)
2378  {
2379  // find the closest approach between a trajectory tj and a point (x,y). Returns
2380  // the index of the closest trajectory point and the distance. Returns false if none
2381  // of the points on the tj are within DOCA
2382 
2383  float close2 = DOCA * DOCA;
2384  closePt = 0;
2385  bool foundClose = false;
2386 
2387  for(unsigned short ipt = tj.EndPt[0]; ipt < tj.EndPt[1] + 1; ++ipt) {
2388  if(tj.Pts[ipt].Chg == 0) continue;
2389  float dx = tj.Pts[ipt].Pos[0] - x;
2390  if(std::abs(dx) > DOCA) continue;
2391  float dy = tj.Pts[ipt].Pos[1] - y;
2392  if(std::abs(dy) > DOCA) continue;
2393  float sep2 = dx * dx + dy * dy;
2394  if(sep2 < close2) {
2395  close2 = sep2;
2396  closePt = ipt;
2397  foundClose = true;
2398  }
2399  } // ipt
2400 
2401  DOCA = sqrt(close2);
2402  return foundClose;
2403 
2404  } // TrajClosestApproach
2405 
2407  float TwoTPAngle(TrajPoint& tp1, TrajPoint& tp2)
2408  {
2409  // Calculates the angle of a line between two TPs
2410  float dw = tp2.Pos[0] - tp1.Pos[0];
2411  float dt = tp2.Pos[1] - tp1.Pos[1];
2412  return atan2(dw, dt);
2413  } // TwoTPAngle
2414 
2416  std::vector<unsigned int> PutTrajHitsInVector(Trajectory const& tj, HitStatus_t hitRequest)
2417  {
2418  // Put hits in each trajectory point into a flat vector
2419  std::vector<unsigned int> hitVec;
2420 
2421  // special handling for shower trajectories. UseHit isn't valid
2422  if(tj.AlgMod[kShowerTj]) {
2423  for(auto& tp : tj.Pts) hitVec.insert(hitVec.end(), tp.Hits.begin(), tp.Hits.end());
2424  return hitVec;
2425  } // shower Tj
2426 
2427  // reserve under the assumption that there will be one hit per point
2428  hitVec.reserve(tj.Pts.size());
2429  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2430  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
2431  unsigned int iht = tj.Pts[ipt].Hits[ii];
2432  bool useit = (hitRequest == kAllHits);
2433  if(tj.Pts[ipt].UseHit[ii] && hitRequest == kUsedHits) useit = true;
2434  if(!tj.Pts[ipt].UseHit[ii] && hitRequest == kUnusedHits) useit = true;
2435  if(useit) hitVec.push_back(iht);
2436  } // iht
2437  } // ipt
2438  return hitVec;
2439  } // PutTrajHitsInVector
2440 
2442  void TagJunkTj(TCSlice& slc, Trajectory& tj, bool prt)
2443  {
2444  // Characterizes the trajectory as a junk tj even though it may not
2445  // have been reconstructed in FindJunkTraj. The distinguishing feature is
2446  // that it is short and has many used hits in each trajectory point.
2447 
2448  // Don't bother if it is too long
2449  if(tj.Pts.size() > 10) return;
2450  if(tcc.useAlg[kNewStpCuts] && tj.PDGCode == 111) return;
2451  // count the number of points that have many used hits
2452  unsigned short nhm = 0;
2453  unsigned short npwc = 0;
2454  for(auto& tp : tj.Pts) {
2455  if(tp.Chg == 0) continue;
2456  ++npwc;
2457  unsigned short nused = 0;
2458  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2459  if(tp.UseHit[ii]) ++nused;
2460  } // ii
2461  if(nused > 3) ++nhm;
2462  } // tp
2463  // Set the junkTj bit if most of the hits are used in most of the tps
2464  if(nhm > 0.5 * npwc) tj.AlgMod[kJunkTj] = true;
2465  if(prt) mf::LogVerbatim("TC")<<"TGT: T"<<tj.ID<<" npwc "<<npwc<<" nhm "<<nhm<<" junk? "<<tj.AlgMod[kJunkTj];
2466  } // TagJunkTj
2467 
2469  bool HasDuplicateHits(TCSlice& slc, Trajectory const& tj, bool prt)
2470  {
2471  // returns true if a hit is associated with more than one TP
2472  auto tjHits = PutTrajHitsInVector(tj, kAllHits);
2473  for(unsigned short ii = 0; ii < tjHits.size() - 1; ++ii) {
2474  for(unsigned short jj = ii + 1; jj < tjHits.size(); ++jj) {
2475  if(tjHits[ii] == tjHits[jj]) {
2476  if(prt) mf::LogVerbatim("TC")<<"HDH: Hit "<<PrintHit(slc.slHits[ii])<<" is a duplicate "<<ii<<" "<<jj;
2477  return true;
2478  }
2479  } // jj
2480  } // ii
2481  return false;
2482  } // HasDuplicateHits
2483 
2485  void MoveTPToWire(TrajPoint& tp, float wire)
2486  {
2487  // Project TP to a "wire position" Pos[0] and update Pos[1]
2488  if(tp.Dir[0] == 0) return;
2489  float dw = wire - tp.Pos[0];
2490  if(std::abs(dw) < 0.01) return;
2491  tp.Pos[0] = wire;
2492  tp.Pos[1] += dw * tp.Dir[1] / tp.Dir[0];
2493  } // MoveTPToWire
2494 
2496  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)
2497  {
2498  // returns a vector of hits that are within the Window[Pos0][Pos1] in plane.
2499  // Note that hits on wire wireWindow[1] are returned as well. The definition of close
2500  // depends on setting of usePeakTime. If UsePeakTime is true, a hit is considered nearby if
2501  // the PeakTime is within the window. This is shown schematically here where
2502  // the time is on the horizontal axis and a "-" denotes a valid entry
2503  // timeWindow -----------------
2504  // hit PeakTime + close
2505  // hit PeakTime + not close
2506  // If usePeakTime is false, a hit is considered nearby if the hit StartTick and EndTick overlap with the timeWindow
2507  // Time window ---------
2508  // Hit StartTick-EndTick -------- close
2509  // Hit StartTick - EndTick -------- not close
2510 
2511  hitsNear = false;
2512  std::vector<unsigned int> closeHits;
2513  if(plane > slc.firstWire.size() - 1) return closeHits;
2514  // window in the wire coordinate
2515  int loWire = wireWindow[0];
2516  if(loWire < (int)slc.firstWire[plane]) loWire = slc.firstWire[plane];
2517  int hiWire = wireWindow[1];
2518  if(hiWire > (int)slc.lastWire[plane]-1) hiWire = slc.lastWire[plane]-1;
2519  // window in the time coordinate
2520  float minTick = timeWindow[0] / tcc.unitsPerTick;
2521  float maxTick = timeWindow[1] / tcc.unitsPerTick;
2522  for(int wire = loWire; wire <= hiWire; ++wire) {
2523  // Set hitsNear if the wire is dead
2524  if(slc.wireHitRange[plane][wire].first == -2) hitsNear = true;
2525  if(slc.wireHitRange[plane][wire].first < 0) continue;
2526  unsigned int firstHit = (unsigned int)slc.wireHitRange[plane][wire].first;
2527  unsigned int lastHit = (unsigned int)slc.wireHitRange[plane][wire].second;
2528  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
2529  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2530  if(usePeakTime) {
2531  if(hit.PeakTime() < minTick) continue;
2532  if(hit.PeakTime() > maxTick) break;
2533  } else {
2534  int hiLo = minTick;
2535  if(hit.StartTick() > hiLo) hiLo = hit.StartTick();
2536  int loHi = maxTick;
2537  if(hit.EndTick() < loHi) loHi = hit.EndTick();
2538  if(loHi < hiLo) continue;
2539  if(hiLo > loHi) break;
2540  }
2541  hitsNear = true;
2542  bool takeit = (hitRequest == kAllHits);
2543  if(hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) takeit = true;
2544  if(hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) takeit = true;
2545  if(takeit) closeHits.push_back(iht);
2546  } // iht
2547  } // wire
2548  return closeHits;
2549  } // FindCloseHits
2550 
2552  bool FindCloseHits(TCSlice& slc, TrajPoint& tp, float const& maxDelta, HitStatus_t hitRequest)
2553  {
2554  // Fills tp.Hits sets tp.UseHit true for hits that are close to tp.Pos. Returns true if there are
2555  // close hits OR if the wire at this position is dead
2556 
2557  tp.Hits.clear();
2558  tp.UseHit.reset();
2559  if(!WireHitRangeOK(slc, tp.CTP)) {
2560  return false;
2561  }
2562 
2563  geo::PlaneID planeID = DecodeCTP(tp.CTP);
2564  unsigned short ipl = planeID.Plane;
2565  if(tp.Pos[0] < -0.4) return false;
2566  unsigned int wire = std::nearbyint(tp.Pos[0]);
2567  if(wire < slc.firstWire[ipl]) return false;
2568  if(wire > slc.lastWire[ipl]-1) return false;
2569 
2570  // dead wire
2571  if(slc.wireHitRange[ipl][wire].first == -1) {
2572  tp.Environment[kEnvDeadWire] = true;
2573  return true;
2574  }
2575  tp.Environment[kEnvDeadWire] = false;
2576  // live wire with no hits
2577  if(slc.wireHitRange[ipl][wire].first == -2) return false;
2578 
2579  unsigned int firstHit = (unsigned int)slc.wireHitRange[ipl][wire].first;
2580  unsigned int lastHit = (unsigned int)slc.wireHitRange[ipl][wire].second;
2581 
2582  float fwire = wire;
2583  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
2584  if((unsigned int)slc.slHits[iht].InTraj > slc.tjs.size()) continue;
2585  bool useit = (hitRequest == kAllHits);
2586  if(hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
2587  if(hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
2588  if(!useit) continue;
2589  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2590  float ftime = tcc.unitsPerTick * hit.PeakTime();
2591  float delta = PointTrajDOCA(slc, fwire, ftime, tp);
2592  if(delta < maxDelta) tp.Hits.push_back(iht);
2593  } // iht
2594  if(tp.Hits.size() > 16) {
2595  tp.Hits.resize(16);
2596  }
2597  // Set UseHit false. The calling routine should decide if these hits should be used
2598  tp.UseHit.reset();
2599  return (!tp.Hits.empty());
2600 
2601  } // FindCloseHits
2602 
2604  std::vector<int> FindCloseTjs(TCSlice& slc, const TrajPoint& fromTp, const TrajPoint& toTp, const float& maxDelta)
2605  {
2606  // Returns a list of Tj IDs that have hits within distance maxDelta on a line drawn between the two Tps as shown
2607  // graphically here, where a "*" is a Tp and "|" and "-" are the boundaries of the region that is checked
2608  //
2609  // ---------------
2610  // | |
2611  // * *
2612  // | |
2613  // ---------------
2614  // If the wire positions of fromTp and toTp are the same, a different region is checked as shown here
2615  //
2616  // -----------
2617  // | |
2618  // | * |
2619  // | |
2620  // -----------
2621 
2622  std::vector<int> tmp;
2623  if(fromTp.Pos[0] < -0.4 || toTp.Pos[0] < -0.4) return tmp;
2624 
2625  TrajPoint tp;
2626  // Make the tp so that stepping is positive
2627  unsigned int firstWire, lastWire;
2628  if(toTp.Pos[0] > fromTp.Pos[0]) {
2629  if(!MakeBareTrajPoint(slc, fromTp, toTp, tp)) return tmp;
2630  firstWire = std::nearbyint(fromTp.Pos[0]);
2631  lastWire = std::nearbyint(toTp.Pos[0]);
2632  } else if(toTp.Pos[0] < fromTp.Pos[0]) {
2633  if(!MakeBareTrajPoint(slc, toTp, fromTp, tp)) return tmp;
2634  firstWire = std::nearbyint(toTp.Pos[0]);
2635  lastWire = std::nearbyint(fromTp.Pos[0]);
2636  } else {
2637  tp.Pos = fromTp.Pos;
2638  float tmp = fromTp.Pos[0] - maxDelta;
2639  if(tmp < 0) tmp = 0;
2640  firstWire = std::nearbyint(tmp);
2641  tmp = fromTp.Pos[0] + maxDelta;
2642  lastWire = std::nearbyint(tmp);
2643  }
2644 
2645  geo::PlaneID planeID = DecodeCTP(tp.CTP);
2646  unsigned short ipl = planeID.Plane;
2647 
2648  if(firstWire < slc.firstWire[ipl]) firstWire = slc.firstWire[ipl];
2649  if(firstWire > slc.lastWire[ipl]-1) return tmp;
2650  if(lastWire < slc.firstWire[ipl]) return tmp;
2651  if(lastWire > slc.lastWire[ipl]-1) lastWire = slc.lastWire[ipl]-1;
2652 
2653  for(unsigned int wire = firstWire; wire <= lastWire; ++wire) {
2654  if(slc.wireHitRange[ipl][wire].first == -1) continue;
2655  if(slc.wireHitRange[ipl][wire].first == -2) continue;
2656  MoveTPToWire(tp, (float)wire);
2657  // Find the tick range at this position
2658  float minTick = (tp.Pos[1] - maxDelta) / tcc.unitsPerTick;
2659  float maxTick = (tp.Pos[1] + maxDelta) / tcc.unitsPerTick;
2660  unsigned int firstHit = (unsigned int)slc.wireHitRange[ipl][wire].first;
2661  unsigned int lastHit = (unsigned int)slc.wireHitRange[ipl][wire].second;
2662  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
2663  if(slc.slHits[iht].InTraj <= 0) continue;
2664  if((unsigned int)slc.slHits[iht].InTraj > slc.tjs.size()) continue;
2665  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2666  if(hit.PeakTime() < minTick) continue;
2667  // Hits are sorted by increasing time so we can break when maxTick is reached
2668  if(hit.PeakTime() > maxTick) break;
2669  if(std::find(tmp.begin(), tmp.end(), slc.slHits[iht].InTraj) != tmp.end()) continue;
2670  tmp.push_back(slc.slHits[iht].InTraj);
2671  } // iht
2672  } // wire
2673 
2674  return tmp;
2675 
2676  } // FindCloseTjs
2677 
2679  float ElectronLikelihood(TCSlice& slc, Trajectory& tj, float& asym)
2680  {
2681  // returns a number between 0 (not electron-like) and 1 (electron-like)
2682  if(NumPtsWithCharge(slc, tj, false) < 8) return -1;
2683  if(tj.StopFlag[1][kBragg]) return 0;
2684 
2685  unsigned short midPt = 0.5 * (tj.EndPt[0] + tj.EndPt[1]);
2686  double rms0 = 0, rms1 = 0;
2687  unsigned short cnt;
2688  TjDeltaRMS(slc, tj, tj.EndPt[0], midPt, rms0, cnt);
2689  TjDeltaRMS(slc, tj, midPt, tj.EndPt[1], rms1, cnt);
2690  asym = std::abs(rms0 - rms1) / (rms0 + rms1);
2691  float chgFact = (tj.ChgRMS - 0.1) * 5;
2692  float elh = 5 * asym * chgFact;
2693  if(elh > 1) elh = 1;
2694  return elh;
2695  } // ElectronLikelihood
2696 
2698  float ChgFracNearPos(TCSlice& slc, const Point2_t& pos, const std::vector<int>& tjIDs)
2699  {
2700  // returns the fraction of the charge in the region around pos that is associated with
2701  // the list of Tj IDs
2702  if(tjIDs.empty()) return 0;
2703  std::array<int, 2> wireWindow;
2704  Point2_t timeWindow;
2705  // 1/2 size of the region
2706  constexpr float NNDelta = 5;
2707  wireWindow[0] = pos[0] - NNDelta;
2708  wireWindow[1] = pos[0] + NNDelta;
2709  timeWindow[0] = pos[1] - NNDelta;
2710  timeWindow[1] = pos[1] + NNDelta;
2711  // do some checking
2712  for(auto& tjID : tjIDs) if(tjID <= 0 || tjID > (int)slc.tjs.size()) return 0;
2713  // Determine which plane we are in
2714  geo::PlaneID planeID = DecodeCTP(slc.tjs[tjIDs[0]-1].CTP);
2715  // get a list of all hits in this region
2716  bool hitsNear;
2717  std::vector<unsigned int> closeHits = FindCloseHits(slc, wireWindow, timeWindow, planeID.Plane, kAllHits, true, hitsNear);
2718  if(closeHits.empty()) return 0;
2719  float chg = 0;
2720  float tchg = 0;
2721  // Add the hit charge in the box
2722  // All hits in the box, and all hits associated with the Tjs
2723  for(auto& iht : closeHits) {
2724  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
2725  chg += hit.Integral();
2726  if(slc.slHits[iht].InTraj == 0) continue;
2727  if(std::find(tjIDs.begin(), tjIDs.end(), slc.slHits[iht].InTraj) != tjIDs.end()) tchg += hit.Integral();
2728  } // iht
2729  if(chg == 0) return 0;
2730  return tchg / chg;
2731  } // ChgFracNearPos
2732 
2734  float MaxHitDelta(TCSlice& slc, Trajectory& tj)
2735  {
2736  float delta, md = 0;
2737  unsigned short ii;
2738  unsigned int iht;
2739  for(auto& tp : tj.Pts) {
2740  for(ii = 0; ii < tp.Hits.size(); ++ii) {
2741  if(!tp.UseHit[ii]) continue;
2742  iht = tp.Hits[ii];
2743  delta = PointTrajDOCA(slc, iht, tp);
2744  if(delta > md) md = delta;
2745  } // ii
2746  } // pts
2747  return md;
2748  } // MaxHitDelta
2749 
2752  {
2753  // reverse the trajectory
2754  if(tj.Pts.empty()) return;
2755  // reverse the crawling direction flag
2756  tj.StepDir = -tj.StepDir;
2757  tj.DirFOM = 1 - tj.DirFOM;
2758  // Vertices
2759  std::swap(tj.VtxID[0], tj.VtxID[1]);
2760  // trajectory points
2761  std::reverse(tj.Pts.begin(), tj.Pts.end());
2762  // reverse the stop flag
2763  std::reverse(tj.StopFlag.begin(), tj.StopFlag.end());
2764  std::swap(tj.dEdx[0], tj.dEdx[1]);
2765  // reverse the direction vector on all points
2766  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2767  if(tj.Pts[ipt].Dir[0] != 0) tj.Pts[ipt].Dir[0] = -tj.Pts[ipt].Dir[0];
2768  if(tj.Pts[ipt].Dir[1] != 0) tj.Pts[ipt].Dir[1] = -tj.Pts[ipt].Dir[1];
2769  if(tj.Pts[ipt].Ang > 0) {
2770  tj.Pts[ipt].Ang -= M_PI;
2771  } else {
2772  tj.Pts[ipt].Ang += M_PI;
2773  }
2774  } // ipt
2775  if(tj.StartEnd == 0 || tj.StartEnd == 1) tj.StartEnd = 1 - tj.StartEnd;
2776  SetEndPoints(tj);
2777  UpdateMatchStructs(slc, tj.ID, tj.ID);
2778  } // ReverseTraj
2779 
2781  bool PointInsideEnvelope(const Point2_t& Point, const std::vector<Point2_t>& Envelope)
2782  {
2783  // returns true if the Point is within the Envelope polygon. Entries in Envelope are the
2784  // Pos[0], Pos[1] locations of the polygon vertices. This is based on the algorithm that the
2785  // sum of the angles of a vector between a point and the vertices will be 2 * pi for an interior
2786  // point and 0 for an exterior point
2787 
2788  Point2_t p1, p2;
2789  unsigned short nvx = Envelope.size();
2790  double angleSum = 0;
2791  for(unsigned short ii = 0; ii < Envelope.size(); ++ii) {
2792  p1[0] = Envelope[ii][0] - Point[0];
2793  p1[1] = Envelope[ii][1] - Point[1];
2794  p2[0] = Envelope[(ii+1)%nvx][0] - Point[0];
2795  p2[1] = Envelope[(ii+1)%nvx][1] - Point[1];
2796  angleSum += DeltaAngle(p1, p2);
2797  }
2798  if(abs(angleSum) < M_PI) return false;
2799  return true;
2800 
2801  } // InsideEnvelope
2802 
2803 
2805  bool SetMag(Vector2_t& v1, double mag)
2806  {
2807  double den = v1[0] * v1[0] + v1[1] * v1[1];
2808  if(den == 0) return false;
2809  den = sqrt(den);
2810 
2811  v1[0] *= mag / den;
2812  v1[1] *= mag / den;
2813  return true;
2814  } // SetMag
2815 
2817  void FindAlongTrans(Point2_t pos1, Vector2_t dir1, Point2_t pos2, Point2_t& alongTrans)
2818  {
2819  // Calculate the distance along and transverse to the direction vector dir1 from pos1 to pos2
2820  alongTrans[0] = 0;
2821  alongTrans[1] = 0;
2822  if(pos1[0] == pos2[0] && pos1[1] == pos2[1]) return;
2823  pos1[0] = pos2[0] - pos1[0];
2824  pos1[1] = pos2[1] - pos1[1];
2825  double sep = sqrt(pos1[0] * pos1[0] + pos1[1] * pos1[1]);
2826  if(sep < 1E-6) return;
2827  Vector2_t ptDir;
2828  ptDir[0] = pos1[0] / sep;
2829  ptDir[1] = pos1[1] / sep;
2830  SetMag(dir1, 1.0);
2831  double costh = DotProd(dir1, ptDir);
2832  if(costh > 1.0 || costh < -1.0) return;
2833  alongTrans[0] = costh * sep;
2834  double sinth = sqrt(1 - costh * costh);
2835  alongTrans[1] = sinth * sep;
2836  } // FindAlongTrans
2837 
2839  double DeltaAngle(const Point2_t& p1, const Point2_t& p2)
2840  {
2841  // angle between two points
2842  double ang1 = atan2(p1[1], p1[0]);
2843  double ang2 = atan2(p2[1], p2[0]);
2844  return DeltaAngle2(ang1, ang2);
2845  } // DeltaAngle
2846 
2848  double DeltaAngle2(double Ang1, double Ang2)
2849  {
2850  constexpr double twopi = 2 * M_PI;
2851  double dang = Ang1 - Ang2;
2852  while(dang > M_PI) dang -= twopi;
2853  while(dang < -M_PI) dang += twopi;
2854  return dang;
2855  }
2856 
2858  double DeltaAngle(double Ang1, double Ang2)
2859  {
2860  return std::abs(std::remainder(Ang1 - Ang2, M_PI));
2861  }
2862 
2865  {
2866  // Find the first (last) TPs, EndPt[0] (EndPt[1], that have charge
2867 
2868  // don't mess with showerTjs or halo tjs
2869  if(tj.AlgMod[kShowerTj] || tj.AlgMod[kHaloTj]) return;
2870 
2871  tj.EndPt[0] = 0; tj.EndPt[1] = 0;
2872  if(tj.Pts.size() == 0) return;
2873 
2874  // check the end point pointers
2875  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) {
2876  if(tj.Pts[ipt].Chg != 0) {
2877  tj.EndPt[0] = ipt;
2878  break;
2879  }
2880  }
2881  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
2882  unsigned short ipt = tj.Pts.size() - 1 - ii;
2883  if(tj.Pts[ipt].Chg != 0) {
2884  tj.EndPt[1] = ipt;
2885  break;
2886  }
2887  }
2888  } // SetEndPoints
2889 
2891  bool TrajIsClean(TCSlice& slc, Trajectory& tj, bool prt)
2892  {
2893  // Returns true if the trajectory has low hit multiplicity and is in a
2894  // clean environment
2895  unsigned short nUsed = 0;
2896  unsigned short nTotHits = 0;
2897  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
2898  TrajPoint& tp = tj.Pts[ipt];
2899  nTotHits += tp.Hits.size();
2900  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
2901  if(tp.UseHit[ii]) ++nUsed;
2902  } // ii
2903  } // ipt
2904  if(nTotHits == 0) return false;
2905  float fracUsed = (float)nUsed / (float)nTotHits;
2906  if(prt) mf::LogVerbatim("TC")<<"TrajIsClean: nTotHits "<<nTotHits<<" nUsed "<<nUsed<<" fracUsed "<<fracUsed;
2907 
2908  if(fracUsed > 0.9) return true;
2909  return false;
2910 
2911  } // TrajIsClean
2912 
2914  short MCSMom(TCSlice& slc, const std::vector<int>& tjIDs)
2915  {
2916  // Find the average MCSMom of the trajectories
2917  if(tjIDs.empty()) return 0;
2918  float summ = 0;
2919  float suml = 0;
2920  for(auto tjid : tjIDs) {
2921  auto& tj = slc.tjs[tjid - 1];
2922  float npts = tj.EndPt[1] - tj.EndPt[0] + 1;
2923  summ += npts * tj.MCSMom;
2924  suml += npts;
2925  } // tjid
2926  return (short)(summ / suml);
2927  } // MCSMom
2928 
2930  short MCSMom(TCSlice& slc, Trajectory& tj)
2931  {
2932  return MCSMom(slc, tj, tj.EndPt[0], tj.EndPt[1]);
2933  } // MCSMom
2934 
2936  short MCSMom(TCSlice& slc, Trajectory& tj, unsigned short firstPt, unsigned short lastPt)
2937  {
2938  // Estimate the trajectory momentum using Multiple Coulomb Scattering ala PDG RPP
2939 
2940  if(firstPt == lastPt) return 0;
2941  if(firstPt > lastPt) std::swap(firstPt, lastPt);
2942 
2943  firstPt = NearestPtWithChg(slc, tj, firstPt);
2944  lastPt = NearestPtWithChg(slc, tj, lastPt);
2945  if(firstPt >= lastPt) return 0;
2946 
2947  if(firstPt < tj.EndPt[0]) return 0;
2948  if(lastPt > tj.EndPt[1]) return 0;
2949  // Can't do this with only 2 points
2950  if(NumPtsWithCharge(slc, tj, false, firstPt, lastPt) < 3) return 0;
2951  // Ignore junk Tjs
2952  if(tj.AlgMod[kJunkTj]) return 0;
2953 
2954  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
2955  if(tjLen < 1) return 0;
2956  // mom calculated in MeV
2957  double thetaRMS = MCSThetaRMS(slc, tj, firstPt, lastPt);
2958  if(thetaRMS < 0.001) return 999;
2959  double mom = 13.8 * sqrt(tjLen / 14) / thetaRMS;
2960  if(mom > 999) mom = 999;
2961  return (short)mom;
2962  } // MCSMom
2963 
2964 
2966  unsigned short NearestPtWithChg(TCSlice& slc, Trajectory& tj, unsigned short thePt)
2967  {
2968  // returns a point near thePt which has charge
2969  if(thePt > tj.EndPt[1]) return thePt;
2970  if(tj.Pts[thePt].Chg > 0) return thePt;
2971 
2972  short endPt0 = tj.EndPt[0];
2973  short endPt1 = tj.EndPt[1];
2974  for(short off = 1; off < 10; ++off) {
2975  short ipt = thePt + off;
2976  if(ipt <= endPt1 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
2977  ipt = thePt - off;
2978  if(ipt >= endPt0 && tj.Pts[ipt].Chg > 0) return (unsigned short)ipt;
2979  } // off
2980  return thePt;
2981  } // NearestPtWithChg
2982 
2984  float MCSThetaRMS(TCSlice& slc, Trajectory& tj)
2985  {
2986  // This returns the MCS scattering angle expected for one WSE unit of travel along the trajectory.
2987  // It is used to define kink and vertex cuts. This should probably be named something different to
2988  // prevent confusion
2989 
2990  float tps = TrajPointSeparation(tj.Pts[tj.EndPt[0]], tj.Pts[tj.EndPt[1]]);
2991  if(tps < 1) return 1;
2992 
2993  return MCSThetaRMS(slc, tj, tj.EndPt[0], tj.EndPt[1]) / sqrt(tps);
2994 
2995  } // MCSThetaRMS
2996 
2998  double MCSThetaRMS(TCSlice& slc, Trajectory& tj, unsigned short firstPt, unsigned short lastPt)
2999  {
3000  // This returns the MCS scattering angle expected for the length of the trajectory
3001  // spanned by firstPt to lastPt. It is used primarily to calculate MCSMom
3002 
3003  if(firstPt < tj.EndPt[0]) return 1;
3004  if(lastPt > tj.EndPt[1]) return 1;
3005 
3006  firstPt = NearestPtWithChg(slc, tj, firstPt);
3007  lastPt = NearestPtWithChg(slc, tj, lastPt);
3008  if(firstPt >= lastPt) return 1;
3009 
3010  double sigmaS;
3011  unsigned short cnt;
3012  TjDeltaRMS(slc, tj, firstPt, lastPt, sigmaS, cnt);
3013  if(sigmaS < 0) return 1;
3014  // BB 11/26/2018 A bad idea
3015  // require that cnt is a significant fraction of the total number of charged points
3016  // so that we don't get erroneously high MCSMom when there are large gaps.
3017  // This is the number of points expected in the count if there are no gaps
3018 // unsigned short numPts = lastPt - firstPt - 1;
3019  // return the previously calculated value of MCSMom
3020 // if(numPts > 5 && cnt < 0.7 * numPts) return tj.MCSMom;
3021  double tjLen = TrajPointSeparation(tj.Pts[firstPt], tj.Pts[lastPt]);
3022  if(tjLen < 1) return 1;
3023  // Theta_o = 4 * sqrt(3) * sigmaS / path
3024  return (6.8 * sigmaS / tjLen);
3025 
3026  } // MCSThetaRMS
3027 
3029  void TjDeltaRMS(TCSlice& slc, Trajectory& tj, unsigned short firstPt, unsigned short lastPt, double& rms, unsigned short& cnt)
3030  {
3031  // returns the rms scatter of points around a line formed by the firstPt and lastPt of the trajectory
3032 
3033  rms = -1;
3034  if(firstPt < tj.EndPt[0]) return;
3035  if(lastPt > tj.EndPt[1]) return;
3036 
3037  firstPt = NearestPtWithChg(slc, tj, firstPt);
3038  lastPt = NearestPtWithChg(slc, tj, lastPt);
3039  if(firstPt >= lastPt) return;
3040 
3041  TrajPoint tmp;
3042  // make a bare trajectory point to define a line between firstPt and lastPt.
3043  // Use the position of the hits at these points
3044  TrajPoint firstTP = tj.Pts[firstPt];
3045  firstTP.Pos = firstTP.HitPos;
3046  TrajPoint lastTP = tj.Pts[lastPt];
3047  lastTP.Pos = lastTP.HitPos;
3048  if(!MakeBareTrajPoint(slc, firstTP, lastTP, tmp)) return;
3049  // sum up the deviations^2
3050  double dsum = 0;
3051  cnt = 0;
3052  for(unsigned short ipt = firstPt + 1; ipt < lastPt; ++ipt) {
3053  if(tj.Pts[ipt].Chg == 0) continue;
3054  // ignore points with large error
3055  if(tcc.useAlg[kNewStpCuts] && tj.Pts[ipt].HitPosErr2 > 4) continue;
3056  dsum += PointTrajDOCA2(slc, tj.Pts[ipt].HitPos[0], tj.Pts[ipt].HitPos[1], tmp);
3057  ++cnt;
3058  } // ipt
3059  if(cnt < 2) return;
3060  rms = sqrt(dsum / (double)cnt);
3061 
3062  } // TjDeltaRMS
3063 
3065  void TagDeltaRays(TCSlice& slc, const CTP_t& inCTP)
3066  {
3067  // DeltaRayTag vector elements
3068  // [0] = max separation of both endpoints from a muon
3069  // [1] = minimum MCSMom
3070  // [2] = maximum MCSMom
3071 
3072  if(!tcc.useAlg[kDeltaRay]) return;
3073  if(tcc.deltaRayTag[0] < 0) return;
3074  if(tcc.deltaRayTag.size() < 3) return;
3075 
3076  bool prt = (tcc.dbgDeltaRayTag && tcc.dbgSlc);
3077 
3078  // double the user-defined separation cut. We will require that at least one of the ends of
3079  // a delta ray be within the user-defined cut and allow
3080  float maxSep = 2 * tcc.deltaRayTag[0];
3081  float maxMinSep = tcc.deltaRayTag[0];
3082  unsigned short minMom = tcc.deltaRayTag[1];
3083  unsigned short maxMom = tcc.deltaRayTag[2];
3084  unsigned short minMuonLength = 2 * tcc.vtx2DCuts[2];
3085  unsigned short minpts = 4;
3086  if(prt) mf::LogVerbatim("TC")<<"TagDeltaRays: maxSep "<<maxSep<<" maxMinSep "<<maxMinSep<<" Mom range "<<minMom<<" to "<<maxMom<<" minpts "<<minpts;
3087 
3088  for(unsigned short itj = 0; itj < slc.tjs.size(); ++itj) {
3089  Trajectory& muTj = slc.tjs[itj];
3090  if(muTj.CTP != inCTP) continue;
3091  if(muTj.AlgMod[kKilled] || muTj.AlgMod[kHaloTj]) continue;
3092  if(muTj.PDGCode != 13) continue;
3093  if(prt) mf::LogVerbatim("TC")<<"TagDeltaRays: Muon T"<<muTj.ID<<" EndPts "<<PrintPos(slc, muTj.Pts[muTj.EndPt[0]])<<"-"<<PrintPos(slc, muTj.Pts[muTj.EndPt[1]]);
3094  // min length
3095  if(muTj.EndPt[1] - muTj.EndPt[0] < minMuonLength) continue;
3096  auto& mtp0 = muTj.Pts[muTj.EndPt[0]];
3097  auto& mtp1 = muTj.Pts[muTj.EndPt[1]];
3098  // Found a muon, now look for delta rays
3099  for(unsigned short jtj = 0; jtj < slc.tjs.size(); ++jtj) {
3100  if(jtj == itj) continue;
3101  Trajectory& dtj = slc.tjs[jtj];
3102  if(dtj.AlgMod[kKilled] || dtj.AlgMod[kHaloTj]) continue;
3103  if(dtj.CTP != inCTP) continue;
3104  if(dtj.PDGCode == 13) continue;
3105  // MCSMom cut
3106  if(dtj.MCSMom < minMom) continue;
3107  if(dtj.MCSMom > maxMom) continue;
3108  if(dtj.EndPt[1] - dtj.EndPt[0] < minpts) continue;
3109  // some rough cuts to require that the delta ray is within the
3110  // ends of the muon
3111  auto& dtp0 = dtj.Pts[dtj.EndPt[0]];
3112  auto& dtp1 = dtj.Pts[dtj.EndPt[1]];
3113  if(muTj.StepDir > 0) {
3114  if(dtp0.Pos[0] < mtp0.Pos[0]) continue;
3115  if(dtp1.Pos[0] > mtp1.Pos[0]) continue;
3116  } else {
3117  if(dtp0.Pos[0] > mtp0.Pos[0]) continue;
3118  if(dtp1.Pos[0] < mtp1.Pos[0]) continue;
3119  }
3120  // find the minimum separation
3121  float doca = maxMinSep;
3122  unsigned short mpt = 0;
3123  unsigned short dpt = 0;
3124  TrajTrajDOCA(slc, muTj, dtj, mpt, dpt, doca, false);
3125  if(doca == maxMinSep) continue;
3126  auto& dTp = dtj.Pts[dpt];
3127  // cut on the distance from the muon ends
3128  if(PosSep(dTp.Pos, mtp0.Pos) < tcc.vtx2DCuts[2]) continue;
3129  if(PosSep(dTp.Pos, mtp1.Pos) < tcc.vtx2DCuts[2]) continue;
3130  // make an angle cut at this point. A delta-ray should have a small angle
3131  float dang = DeltaAngle(muTj.Pts[mpt].Ang, dtj.Pts[dpt].Ang);
3132  if(prt) mf::LogVerbatim("TC")<<" dRay? T"<<dtj.ID<<" at "<<PrintPos(slc, dtj.Pts[dpt].Pos)<<" dang "<<dang<<" doca "<<doca;
3133  // ignore the angle cut if the separation is small and the delta ray MCSMom is low
3134  bool closeDeltaRay = (doca < 2 && dtj.MCSMom < 20);
3135  if(!closeDeltaRay && dang > tcc.kinkCuts[0]) continue;
3136  unsigned short oend = 0;
3137  // check the delta at the end of the delta-ray that is farthest away from the
3138  // closest point
3139  if(dpt > 0.5 * (dtj.EndPt[0] + dtj.EndPt[1])) oend = 1;
3140  auto& farEndTP = dtj.Pts[dtj.EndPt[oend]];
3141  float farEndDelta = PointTrajDOCA(slc, farEndTP.Pos[0], farEndTP.Pos[1], muTj.Pts[mpt]);
3142  if(prt) mf::LogVerbatim("TC")<<" farEnd "<<PrintPos(slc, farEndTP.Pos)<<" farEndDelta "<<farEndDelta;
3143  if(farEndDelta > maxSep) continue;
3144  if(prt) mf::LogVerbatim("TC")<<" delta ray "<<dtj.ID<<" parent -> "<<muTj.ID;
3145  dtj.ParentID = muTj.ID;
3146  dtj.PDGCode = 11;
3147  dtj.AlgMod[kDeltaRay] = true;
3148  // Set the start of the delta-ray to be end 0
3149  if(oend != 1) ReverseTraj(slc, dtj);
3150  } // jtj
3151  } // itj
3152 
3153  } // TagDeltaRays
3154 
3155 /* The value of using this function should be considered
3157  void TagMuonDirections(TCSlice& slc, short debugWorkID)
3158  {
3159  // Determine muon directions delta-ray proximity to muon trajectories
3160 
3161  if(tcc.muonTag[0] < 0) return;
3162 
3163  unsigned short minLen = tcc.muonTag[3];
3164 
3165  for(unsigned short itj = 0; itj < slc.tjs.size(); ++itj) {
3166  Trajectory& muTj = slc.tjs[itj];
3167  if(muTj.AlgMod[kKilled]) continue;
3168  if(prt) {
3169  mf::LogVerbatim("TC")<<"TagMuonDirection: Muon "<<muTj.CTP<<" "<<PrintPos(slc, muTj.Pts[muTj.EndPt[0]])<<"-"<<PrintPos(slc, muTj.Pts[muTj.EndPt[1]]);
3170  }
3171  if(muTj.PDGCode != 13) continue;
3172  // look for delta ray trajectories and count the number of times that
3173  unsigned short nPos = 0;
3174  unsigned short nNeg = 0;
3175  for(auto& dtj : slc.tjs) {
3176  if(dtj.AlgMod[kKilled]) continue;
3177  if(dtj.ParentID != muTj.ID) continue;
3178  if(dtj.EndPt[1] - dtj.EndPt[0] > minLen) continue;
3179  if(dtj.StepDir > 0) {
3180  ++nPos;
3181  } else {
3182  ++nNeg;
3183  }
3184  } // dtj
3185  if(nPos == nNeg) continue;
3186  if(nPos > nNeg) {
3187  if(muTj.StepDir < 0) ReverseTraj(slc, muTj);
3188  } else {
3189  if(muTj.StepDir > 0) ReverseTraj(slc, muTj);
3190  }
3191 // muTj.AlgMod[kSetDir] = true;
3192  } // itj
3193  } // TagMuonDirections
3194 */
3196  void UpdateTjChgProperties(std::string inFcnLabel, TCSlice& slc, Trajectory& tj, bool prt)
3197  {
3198  // Updates properties of the tj that are affected when the TP environment
3199  // is changed. The most likely reason for a change is when the tj is attached to a
3200  // vertex in which case the Environment kEnvOverlap bit may be set by the UpdateVxEnvironment
3201  // function in which case this function is called.
3202  // The kEnvNearShower bit may be set by TagShowerTjs but this doesn't affect the
3203  // calculation of the properties of this Tj.tcc.maxPos0 This function simply sets the kEnvUnusedHits bit
3204  // for all TPs.
3205  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) return;
3206 
3207  std::string fcnLabel = inFcnLabel + ".UpTjProp";
3208 
3209  // first (un)set some bits
3210  for(auto& tp : tj.Pts) {
3211  if(tp.Chg <= 0) continue;
3212  tp.Environment[kEnvUnusedHits] = false;
3213  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3214  if(tp.UseHit[ii]) continue;
3215  unsigned int iht = tp.Hits[ii];
3216  if(slc.slHits[iht].InTraj == 0) {
3217  tp.Environment[kEnvUnusedHits] = true;
3218  } else {
3219  tp.Environment[kEnvNearTj] = true;
3220  }
3221  } // ii
3222  } // tp
3223 
3224  // Update the tj charge variables. The concept is explained by this graphic where
3225  // each column is a wire, Q = a TP with charge, q = a TP with charge that is an
3226  // EnvOverlap region, x = a wire that has a TP with Chg = 0 or a wire that has no TP
3227  // because the wire is dead, o = an EnvOverlap region, V = vertex attached to end. You should
3228  // imagine that all 3 tjs come from the same vertex
3229  // 01234567890123456789 npwc cnt range
3230  // VooooQQQQxxxQQQ 7 7 0 - 14
3231  // VqqqqQQQQxxxQQQQQQQQ 16 12 0 - 19
3232  // VooQQQ 3 3 0 - 5
3233  // The average is first calculated using Ave = sum(Q) / npwc
3234  // TotChg is calculated using
3235  tj.TotChg = 0;
3236  tj.AveChg = 0;
3237  tj.ChgRMS = 0.5;
3238 
3239  // These variables are used to calculate the average and rms using valid points with charge
3240  double vcnt = 0;
3241  double vsum = 0;
3242  double vsum2 = 0;
3243  // Reject a single large charge TP
3244  float bigChg = 0;
3245  for(unsigned short ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1]; ++ipt) {
3246  auto& tp = tj.Pts[ipt];
3247  if(tp.Chg > bigChg) bigChg = tp.Chg;
3248  } // ipt
3249  // variables for calculating the backup quanties. These are only used if npwc < 3
3250  double bcnt = 0;
3251  double bsum = 0;
3252  double bsum2 = 0;
3253  // don't include the end points
3254  for(unsigned short ipt = tj.EndPt[0] + 1; ipt < tj.EndPt[1]; ++ipt) {
3255  auto& tp = tj.Pts[ipt];
3256  if(tp.Chg <= 0) continue;
3257  // ignore the single large charge TP
3258  if(tp.Chg == bigChg) continue;
3259  // accumulate a backup sum in case most of the points are overlapped. Note that
3260  // tp.Chg has an angle correction, which is why the hit integral is summed
3261  // below. We don't care about this detail for the backup sum
3262  bsum += tp.Chg;
3263  bsum2 += tp.Chg * tp.Chg;
3264  if(tp.Chg > bigChg) bigChg = tp.Chg;
3265  ++bcnt;
3266  // Skip TPs that overlap with TPs on other Tjs. A correction will be made below
3267  if(tj.Pts[ipt].Environment[kEnvOverlap]) continue;
3268  ++vcnt;
3269  double tpchg = 0;
3270  for(unsigned short ii = 0; ii < tj.Pts[ipt].Hits.size(); ++ii) {
3271  if(!tp.UseHit[ii]) continue;
3272  unsigned int iht = tp.Hits[ii];
3273  tpchg += (*evt.allHits)[slc.slHits[iht].allHitsIndex].Integral();
3274  } // ii
3275  vsum += tpchg;
3276  vsum2 += tpchg * tpchg;
3277  } // ipt
3278 
3279  if(bcnt == 0) return;
3280 
3281  if(vcnt < 3) {
3282  // use the backup sum
3283  tj.TotChg = bsum;
3284  tj.AveChg = bsum / bcnt;
3285  if(vcnt > 2) {
3286  double arg = bsum2 - bcnt * tj.AveChg * tj.AveChg;
3287  if(arg > 0) tj.ChgRMS = sqrt(arg / (bcnt - 1));
3288  }
3289  for(auto& tp : tj.Pts) tp.AveChg = tj.AveChg;
3290  return;
3291  } // low npwc
3292 
3293  double nWires = tj.EndPt[1] - tj.EndPt[0] + 1;
3294  if(nWires < 2) return;
3295  // correct for wires missing near vertices.
3296  // Count the number of wires between vertices at the ends and the first wire
3297  // that has charge. This code assumes that there should be one TP on each wire
3298  if(!tj.AlgMod[kPhoton]) {
3299  for(unsigned short end = 0; end < 2; ++end) {
3300  if(tj.VtxID[end] == 0) continue;
3301  auto& tp = tj.Pts[tj.EndPt[end]];
3302  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
3303  int dw = std::abs(tp.Pos[0] - vx2.Pos[0]);
3304  // This assumes that the vertex is not inside the wire boundaries of the tj
3305  nWires += dw;
3306  } // end
3307  } // not a photon Tj
3308 
3309  tj.AveChg = vsum / vcnt;
3310  // calculate the total charge using the tj wire range
3311  tj.TotChg = nWires * tj.AveChg;
3312  // calculate the rms
3313  double arg = vsum2 - vcnt * tj.AveChg * tj.AveChg;
3314  double rms = 0.5;
3315  if(arg > 0) rms = sqrt(arg / (vcnt - 1));
3316  rms /= tj.AveChg;
3317  // don't let it be an unrealistically low value. It could be crazy large however.
3318  if(rms < 0.1) rms = 0.1;
3319  // Don't let the calculated charge RMS dominate until it is well known; after there are 5 - 10 valid TPs.
3320  // Set the starting charge rms = 0.5
3321  if(vcnt < 10) {
3322  double defFrac = 1 / vcnt;
3323  rms = defFrac * 0.5 + (1 - defFrac) * rms;
3324  }
3325  tj.ChgRMS = rms;
3326 
3327  // Update the TP charge pulls.
3328  // Don't let the calculated charge RMS dominate the default
3329  // RMS until it is well known. Start with 50% error on the
3330  // charge RMS
3331  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
3332  auto& tp = tj.Pts[ipt];
3333  if(tp.Chg <= 0) continue;
3334  tp.ChgPull = (tp.Chg / tj.AveChg - 1) / tj.ChgRMS;
3335  } // ipt
3336 
3337  // update the local charge average using NPtsAve of the preceding points.
3338  // Handle short Tjs first.
3339  if(vcnt < tcc.nPtsAve) {
3340  for(auto& tp : tj.Pts) tp.AveChg = tj.AveChg;
3341  return;
3342  }
3343 
3344  // Set the local average to 0 first
3345  for(auto& tp : tj.Pts) tp.AveChg = 0;
3346  // Enter the local average on the points where an average can be calculated
3347  unsigned short nptsave = tcc.nPtsAve;
3348  unsigned short minPt = tj.EndPt[0] + nptsave;
3349  float lastAve = 0;
3350  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3351  unsigned short ipt = tj.EndPt[1] - ii;
3352  if(ipt < minPt) break;
3353  float cnt = 0;
3354  float sum = 0;
3355  for(unsigned short iii = 0; iii < nptsave; ++iii) {
3356  unsigned short iipt = ipt - iii;
3357  // Don't include the charge of the first point
3358  if(iipt == tj.EndPt[0]) break;
3359  auto& tp = tj.Pts[iipt];
3360  if(tp.Chg <= 0) continue;
3361  sum += tp.Chg;
3362  ++cnt;
3363  } // iii
3364  if(cnt > 2) {
3365  tj.Pts[ipt].AveChg = sum / cnt;
3366  lastAve = tj.Pts[ipt].AveChg;
3367  }
3368  } // ii
3369  // Fill in the points where no average was calculated
3370  for(unsigned short ii = tj.EndPt[0]; ii <= tj.EndPt[1]; ++ii) {
3371  unsigned short ipt = tj.EndPt[1] - ii;
3372  auto& tp = tj.Pts[ipt];
3373  if(tp.AveChg == 0) {
3374  tp.AveChg = lastAve;
3375  } else {
3376  lastAve = tp.AveChg;
3377  }
3378  } // ii
3379 
3380  tj.NeedsUpdate = false;
3381 
3382  } // UpdateTjChgProperties
3383 
3385  void UpdateVxEnvironment(std::string inFcnLabel, TCSlice& slc, VtxStore& vx2, bool prt)
3386  {
3387  // Update the Environment each TP on trajectories near the vertex. This is called when
3388  // the Tj has been added to a vertex to identify nearby trajectories that contribute to
3389  // the charge on TPs near the vertex.
3390  if(vx2.ID == 0) return;
3391  if(vx2.Stat[kOnDeadWire]) return;
3392 
3393  std::string fcnLabel = inFcnLabel + ".UpVxProp";
3394 
3395  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<" UpdateTjEnvironment check Tjs attached to vx2 "<<vx2.ID;
3396 
3397  std::vector<int> tjlist;
3398  std::vector<unsigned short> tjends;
3399  if(vx2.Pos[0] < -0.4) return;
3400  unsigned int vxWire = std::nearbyint(vx2.Pos[0]);
3401  unsigned int loWire = vxWire;
3402  unsigned int hiWire = vxWire;
3403  for(auto& tj : slc.tjs) {
3404  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
3405  if(tj.CTP != vx2.CTP) continue;
3406  // ignore photon Tjs
3407  if(tj.AlgMod[kPhoton]) continue;
3408  for(unsigned short end = 0; end < 2; ++end) {
3409  if(tj.VtxID[end] != vx2.ID) continue;
3410  tjlist.push_back(tj.ID);
3411  tjends.push_back(end);
3412  if(tj.Pts[tj.EndPt[end]].Pos[0] < -0.4) return;
3413  unsigned int endWire = std::nearbyint(tj.Pts[tj.EndPt[end]].Pos[0]);
3414  if(endWire < loWire) loWire = endWire;
3415  if(endWire > hiWire) hiWire = endWire;
3416  } // end
3417  } // tj
3418  if(tjlist.size() < 2) return;
3419  if(hiWire < loWire + 1) return;
3420  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<" check Tjs on wires in the range "<<loWire<<" to "<<hiWire;
3421 
3422  // create a vector of TPs between loWire and hiWire for every tj in the list
3423  // wire TP
3424  std::vector<std::vector<TrajPoint>> wire_tjpt;
3425  // companion vector of IDs
3426  std::vector<int> tjids;
3427  // populate this vector with TPs on Tjs that are in this range
3428  unsigned short nwires = hiWire - loWire + 1;
3429  for(unsigned short itj = 0; itj < tjlist.size(); ++itj) {
3430  auto& tj = slc.tjs[tjlist[itj] - 1];
3431  unsigned short end = tjends[itj];
3432  std::vector<TrajPoint> tjpt(nwires);
3433  // first enter valid TPs in the range
3434  for(unsigned short ii = 0; ii < tj.Pts.size(); ++ii) {
3435  unsigned short ipt;
3436  if(end == 0) { ipt = tj.EndPt[0] + ii; } else { ipt = tj.EndPt[1] - ii; }
3437  if(ipt > tj.Pts.size() - 1) break;
3438  // Make a copy of the TP so we can alter it
3439  auto tp = tj.Pts[ipt];
3440  if(tp.Chg <= 0) continue;
3441  tp.Chg = 1;
3442  tp.Hits.clear();
3443  if(tp.Pos[0] < -0.4) continue;
3444  unsigned int wire = std::nearbyint(tp.Pos[0]);
3445  unsigned short indx = wire - loWire;
3446  if(indx > nwires - 1) break;
3447  tp.Step = ipt;
3448  // We will use NTPsFit to count the number of neighboring TPs
3449  tp.NTPsFit = 0;
3450  tjpt[indx] = tp;
3451  } // ii
3452  // next make TPs on the wires that don't have real TPs
3453  TrajPoint ltp;
3454  // put ltp at the vertex position with direction towards the end point
3455  MakeBareTrajPoint(vx2.Pos, tj.Pts[tj.EndPt[end]].Pos, ltp);
3456  if(ltp.Dir[0] == 0) continue;
3457  if(ltp.Pos[0] < -0.4) continue;
3458  unsigned int wire = std::nearbyint(ltp.Pos[0]);
3459  ltp.Chg = 0;
3460  unsigned short indx = wire - loWire;
3461  // Break if we found a real TP
3462  if(tjpt[indx].Chg == 0) tjpt[indx] = ltp;
3463  double stepSize = std::abs(1/ltp.Dir[0]);
3464  for(unsigned short ii = 0; ii < nwires; ++ii) {
3465  // move the local TP position by one step in the right direction
3466  for(unsigned short iwt = 0; iwt < 2; ++iwt) ltp.Pos[iwt] += ltp.Dir[iwt] * stepSize;
3467  if(ltp.Pos[0] < -0.4) break;
3468  wire = std::nearbyint(ltp.Pos[0]);
3469  if(wire < loWire || wire > hiWire) break;
3470  indx = wire - loWire;
3471  if(tjpt[indx].Chg > 0) continue;
3472  tjpt[indx]= ltp;
3473  } // ii
3474  if(prt) {
3475  mf::LogVerbatim myprt("TC");
3476  myprt<<fcnLabel<<" T"<<tj.ID;
3477  for(auto& tp : tjpt) myprt<<" "<<PrintPos(slc, tp.Pos)<<"_"<<tp.Step<<"_"<<(int)tp.Chg;
3478  }
3479  wire_tjpt.push_back(tjpt);
3480  tjids.push_back(tj.ID);
3481  } // itj
3482 
3483  // iterate over the wires in the range
3484  for(unsigned short indx = 0; indx < nwires; ++indx) {
3485  // count the number of valid points on this wire
3486  unsigned short npts = 0;
3487  // count the number of points on this wire that have charge
3488  unsigned short npwc = 0;
3489  for(unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
3490  if(wire_tjpt[itj][indx].Pos[0] == 0) continue;
3491  // found a valid point
3492  ++npts;
3493  if(wire_tjpt[itj][indx].Chg > 0) ++npwc;
3494  } // itj
3495  // no valid points
3496 // if(prt) mf::LogVerbatim("TC")<<" wire "<<loWire + indx<<" npts "<<npts<<" npwc "<<npwc;
3497  if(npts == 0) continue;
3498  // all valid points have charge
3499  if(npwc == npts) continue;
3500  // re-find the valid points with charge and set the kEnvOverlap bit
3501  for(unsigned short itj = 0; itj < wire_tjpt.size(); ++itj) {
3502  if(wire_tjpt[itj][indx].Pos[0] == 0) continue;
3503  if(wire_tjpt[itj][indx].Chg == 0) continue;
3504  auto& tj = slc.tjs[tjids[itj] - 1];
3505  unsigned short ipt = wire_tjpt[itj][indx].Step;
3506  tj.Pts[ipt].Environment[kEnvOverlap] = true;
3507  tj.NeedsUpdate = true;
3508  if(prt) mf::LogVerbatim("TC")<<fcnLabel<<" Set kEnvOverlap bit on T"<<tj.ID<<" ipt "<<ipt;
3509  } // itj
3510  } // indx
3511 
3512  // update the charge rms for those tjs whose environment was changed above (or elsewhere)
3513  for(auto tjid : tjids) {
3514  auto& tj = slc.tjs[tjid - 1];
3515  if(!tj.NeedsUpdate) continue;
3516  if(tj.CTP != vx2.CTP) continue;
3517  UpdateTjChgProperties(fcnLabel, slc, tj, prt);
3518  } // tjid
3519 
3520  } // UpdateVxEnvironment
3521 
3524  {
3525  // Projects the space point defined by pos and dir into the CTP and returns it in the form of a trajectory point.
3526  // The TP Pos[0] is set to a negative number if the point has an invalid wire position but doesn't return an
3527  // error if the position is on a dead wire. The projection of the direction vector in CTP is stored in tp.Delta.
3528  TrajPoint tp;
3529  tp.Pos = {{0,0}};
3530  tp.Dir = {{0,1}};
3531  tp.CTP = inCTP;
3532  geo::PlaneID planeID = DecodeCTP(inCTP);
3533 
3534  tp.Pos[0] = tcc.geom->WireCoordinate(pos[1], pos[2], planeID);
3535  if(tp.Pos[0] < 0 || (!tcc.maxPos0.empty() && tp.Pos[0] > tcc.maxPos0[planeID.Plane])) {
3536  tp.Pos[0] = -1;
3537  return tp;
3538  }
3539  tp.Pos[1] = tcc.detprop->ConvertXToTicks(pos[0], planeID) * tcc.unitsPerTick;
3540 
3541  // now find the direction if dir is defined
3542  if(dir[0] == 0 && dir[1] == 0 && dir[2] == 0) return tp;
3543  // Make a point at the origin and one 100 units away
3544  // BUG the double brace syntax is required to work around clang bug 21629
3545  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
3546  Point3_t ori3 = {{0.0, 0.0, 0.0}};
3547  Point3_t pos3 = {{100 * dir[0], 100 * dir[1], 100 * dir[2]}};
3548  // 2D position of ori3 and the pos3 projection
3549  std::array<double, 2> ori2;
3550  std::array<double, 2> pos2;
3551  std::array<double, 2> dir2;
3552  // the wire coordinates
3553  ori2[0] = tcc.geom->WireCoordinate(ori3[1], ori3[2], planeID);
3554  pos2[0] = tcc.geom->WireCoordinate(pos3[1], pos3[2], planeID);
3555  // the time coordinates
3556  ori2[1] = tcc.detprop->ConvertXToTicks(ori3[0], planeID) * tcc.unitsPerTick;
3557  pos2[1] = tcc.detprop->ConvertXToTicks(pos3[0], planeID) * tcc.unitsPerTick;
3558 
3559  dir2[0] = pos2[0] - ori2[0];
3560  dir2[1] = pos2[1] - ori2[1];
3561 
3562  double norm = sqrt(dir2[0] * dir2[0] + dir2[1] * dir2[1]);
3563  tp.Dir[0] = dir2[0] / norm;
3564  tp.Dir[1] = dir2[1] / norm;
3565  tp.Ang = atan2(dir2[1], dir2[0]);
3566  tp.Delta = norm / 100;
3567 
3568  // The Orth vectors are not unit normalized so we need to correct for this
3569  double w0 = tcc.geom->WireCoordinate(0, 0, planeID);
3570  // cosine-like component
3571  double cs = tcc.geom->WireCoordinate(1, 0, planeID) - w0;
3572  // sine-like component
3573  double sn = tcc.geom->WireCoordinate(0, 1, planeID) - w0;
3574  norm = sqrt(cs * cs + sn * sn);
3575  tp.Delta /= norm;
3576 
3577  return tp;
3578  } // MakeBareTP
3579 
3581  bool MakeBareTrajPoint(TCSlice& slc, unsigned int fromHit, unsigned int toHit, TrajPoint& tp)
3582  {
3583  if(fromHit > slc.slHits.size() - 1) return false;
3584  if(toHit > slc.slHits.size() - 1) return false;
3585  auto& fhit = (*evt.allHits)[slc.slHits[fromHit].allHitsIndex];
3586  auto& thit = (*evt.allHits)[slc.slHits[toHit].allHitsIndex];
3587  CTP_t tCTP = EncodeCTP(fhit.WireID());
3588  return MakeBareTrajPoint(slc, (float)fhit.WireID().Wire, fhit.PeakTime(),
3589  (float)thit.WireID().Wire, thit.PeakTime(), tCTP, tp);
3590 
3591  } // MakeBareTrajPoint
3592 
3594  bool MakeBareTrajPoint(TCSlice& slc, float fromWire, float fromTick, float toWire, float toTick, CTP_t tCTP, TrajPoint& tp)
3595  {
3596  tp.CTP = tCTP;
3597  tp.Pos[0] = fromWire;
3598  tp.Pos[1] = tcc.unitsPerTick * fromTick;
3599  tp.Dir[0] = toWire - fromWire;
3600  tp.Dir[1] = tcc.unitsPerTick * (toTick - fromTick);
3601  double norm = sqrt(tp.Dir[0] * tp.Dir[0] + tp.Dir[1] * tp.Dir[1]);
3602  if(norm == 0) return false;
3603  tp.Dir[0] /= norm;
3604  tp.Dir[1] /= norm;
3605  tp.Ang = atan2(tp.Dir[1], tp.Dir[0]);
3606  return true;
3607  } // MakeBareTrajPoint
3608 
3610  bool MakeBareTrajPoint(const Point2_t& fromPos, const Point2_t& toPos, TrajPoint& tpOut)
3611  {
3612  tpOut.Pos = fromPos;
3613  tpOut.Dir = PointDirection(fromPos, toPos);
3614 /*
3615  tpOut.Dir[0] = toPos[0] - fromPos[0];
3616  tpOut.Dir[1] = toPos[1] - fromPos[1];
3617  double norm = sqrt(tpOut.Dir[0] * tpOut.Dir[0] + tpOut.Dir[1] * tpOut.Dir[1]);
3618  if(norm == 0) return false;
3619  tpOut.Dir[0] /= norm;
3620  tpOut.Dir[1] /= norm;
3621 */
3622  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
3623  return true;
3624 
3625  } // MakeBareTrajPoint
3626 
3628  bool MakeBareTrajPoint(TCSlice& slc, const TrajPoint& tpIn1, const TrajPoint& tpIn2, TrajPoint& tpOut)
3629  {
3630  tpOut.CTP = tpIn1.CTP;
3631  tpOut.Pos = tpIn1.Pos;
3632  tpOut.Dir = PointDirection(tpIn1.Pos, tpIn2.Pos);
3633 /*
3634  tpOut.Dir[0] = tpIn2.Pos[0] - tpIn1.Pos[0];
3635  tpOut.Dir[1] = tpIn2.Pos[1] - tpIn1.Pos[1];
3636  double norm = sqrt(tpOut.Dir[0] * tpOut.Dir[0] + tpOut.Dir[1] * tpOut.Dir[1]);
3637  if(norm == 0) return false;
3638  tpOut.Dir[0] /= norm;
3639  tpOut.Dir[1] /= norm;
3640 */
3641  tpOut.Ang = atan2(tpOut.Dir[1], tpOut.Dir[0]);
3642  return true;
3643  } // MakeBareTrajPoint
3644 
3646  unsigned short FarEnd(TCSlice& slc, const Trajectory& tj, const Point2_t& pos)
3647  {
3648  // Returns the end (0 or 1) of the Tj that is furthest away from the position pos
3649  if(tj.ID == 0) return 0;
3650  if(PosSep2(tj.Pts[tj.EndPt[1]].Pos, pos) > PosSep2(tj.Pts[tj.EndPt[0]].Pos, pos)) return 1;
3651  return 0;
3652  } // FarEnd
3653 
3656  {
3657  // Finds the direction vector between the two points from p1 to p2
3658  Vector2_t dir;
3659  for(unsigned short xyz = 0; xyz < 2; ++xyz) dir[xyz] = p2[xyz] - p1[xyz];
3660  if(dir[0] == 0 && dir[1] == 0) return dir;
3661  double norm = sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
3662  dir[0] /= norm;
3663  dir[1] /= norm;
3664  return dir;
3665  } // PointDirection
3666 
3668  float TPHitsRMSTime(TCSlice& slc, TrajPoint& tp, HitStatus_t hitRequest)
3669  {
3670  return tcc.unitsPerTick * TPHitsRMSTick(slc, tp, hitRequest);
3671  } // TPHitsRMSTime
3672 
3674  float TPHitsRMSTick(TCSlice& slc, TrajPoint& tp, HitStatus_t hitRequest)
3675  {
3676  // Estimate the RMS of all hits associated with a trajectory point
3677  // without a lot of calculation
3678  if(tp.Hits.empty()) return 0;
3679  float minVal = 9999;
3680  float maxVal = 0;
3681  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3682  bool useit = (hitRequest == kAllHits);
3683  if(hitRequest == kUsedHits && tp.UseHit[ii]) useit = true;
3684  if(hitRequest == kUnusedHits && !tp.UseHit[ii]) useit = true;
3685  if(!useit) continue;
3686  unsigned int iht = tp.Hits[ii];
3687  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
3688  float cv = hit.PeakTime();
3689  float rms = hit.RMS();
3690  float arg = cv - rms;
3691  if(arg < minVal) minVal = arg;
3692  arg = cv + rms;
3693  if(arg > maxVal) maxVal = arg;
3694  } // ii
3695  if(maxVal == 0) return 0;
3696  return (maxVal - minVal) / 2;
3697  } // TPHitsRMSTick
3698 
3700  float HitsRMSTime(TCSlice& slc, const std::vector<unsigned int>& hitsInMultiplet, HitStatus_t hitRequest)
3701  {
3702  return tcc.unitsPerTick * HitsRMSTick(slc, hitsInMultiplet, hitRequest);
3703  } // HitsRMSTick
3704 
3706  float HitsRMSTick(TCSlice& slc, const std::vector<unsigned int>& hitsInMultiplet, HitStatus_t hitRequest)
3707  {
3708  if(hitsInMultiplet.empty()) return 0;
3709 
3710  if(hitsInMultiplet.size() == 1) {
3711  auto& hit = (*evt.allHits)[slc.slHits[hitsInMultiplet[0]].allHitsIndex];
3712  return hit.RMS();
3713  }
3714 
3715  float minVal = 9999;
3716  float maxVal = 0;
3717  for(unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
3718  unsigned int iht = hitsInMultiplet[ii];
3719  bool useit = (hitRequest == kAllHits);
3720  if(hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
3721  if(hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
3722  if(!useit) continue;
3723  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
3724  float cv = hit.PeakTime();
3725  float rms = hit.RMS();
3726  float arg = cv - rms;
3727  if(arg < minVal) minVal = arg;
3728  arg = cv + rms;
3729  if(arg > maxVal) maxVal = arg;
3730  } // ii
3731  if(maxVal == 0) return 0;
3732  return (maxVal - minVal) / 2;
3733  } // HitsRMSTick
3734 
3736  float HitsPosTime(TCSlice& slc, const std::vector<unsigned int>& hitsInMultiplet, float& sum, HitStatus_t hitRequest)
3737  {
3738  return tcc.unitsPerTick * HitsPosTick(slc, hitsInMultiplet, sum, hitRequest);
3739  } // HitsPosTime
3740 
3742  float HitsPosTick(TCSlice& slc, const std::vector<unsigned int>& hitsInMultiplet, float& sum, HitStatus_t hitRequest)
3743  {
3744  // returns the position and the charge
3745  float pos = 0;
3746  sum = 0;
3747  for(unsigned short ii = 0; ii < hitsInMultiplet.size(); ++ii) {
3748  unsigned int iht = hitsInMultiplet[ii];
3749  bool useit = (hitRequest == kAllHits);
3750  if(hitRequest == kUsedHits && slc.slHits[iht].InTraj > 0) useit = true;
3751  if(hitRequest == kUnusedHits && slc.slHits[iht].InTraj == 0) useit = true;
3752  if(!useit) continue;
3753  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
3754  float chg = hit.Integral();
3755  pos += chg * hit.PeakTime();
3756  sum += chg;
3757  } // ii
3758  if(sum == 0) return 0;
3759  return pos / sum;
3760  } // HitsPosTick
3761 
3763  unsigned short NumUsedHitsInTj(TCSlice& slc, const Trajectory& tj)
3764  {
3765  if(tj.AlgMod[kKilled]) return 0;
3766  if(tj.Pts.empty()) return 0;
3767  unsigned short nhits = 0;
3768  for(auto& tp : tj.Pts) {
3769  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) if(tp.UseHit[ii]) ++nhits;
3770  } // tp
3771  return nhits;
3772  } // NumHitsInTj
3773 
3775  unsigned short NumHitsInTP(const TrajPoint& tp, HitStatus_t hitRequest)
3776  {
3777  // Counts the number of hits of the specified type in tp
3778  if(tp.Hits.empty()) return 0;
3779 
3780  if(hitRequest == kAllHits) return tp.Hits.size();
3781 
3782  unsigned short nhits = 0;
3783  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
3784  if(hitRequest == kUsedHits) {
3785  if(tp.UseHit[ii]) ++nhits;
3786  } else {
3787  // looking for unused hits
3788  if(!tp.UseHit[ii]) ++nhits;
3789  }
3790  } // ii
3791  return nhits;
3792  } // NumHitsInTP
3793 
3795  void SetPDGCode(TCSlice& slc, unsigned short itj, bool tjDone)
3796  {
3797  if(itj > slc.tjs.size() - 1) return;
3798  SetPDGCode(slc, slc.tjs[itj], tjDone);
3799  }
3800 
3802  void SetPDGCode(TCSlice& slc, Trajectory& tj, bool tjDone)
3803  {
3804  // Sets the PDG code for the supplied trajectory. Note that the existing
3805  // PDG code is left unchanged if these cuts are not met
3806 
3807  short npwc = NumPtsWithCharge(slc, tj, false);
3808  if(npwc < 6) {
3809  tj.PDGCode = 0;
3810  return;
3811  }
3812 
3813  if(tj.Strategy[kStiffEl]) {
3814  tj.PDGCode = 111;
3815  return;
3816  }
3817  if(tj.Strategy[kStiffMu]) {
3818  tj.PDGCode = 13;
3819  return;
3820  }
3821 
3822 // tj.PDGCode = 0;
3823  if(tcc.muonTag[0] <= 0) return;
3824  // Special handling of very long straight trajectories, e.g. uB cosmic rays
3825  bool isAMuon = (npwc > (unsigned short)tcc.muonTag[0] && tj.MCSMom > tcc.muonTag[1]);
3826  // anything really really long must be a muon
3827  if(npwc > 500) isAMuon = true;
3828  if(tj.PDGCode != 0 && tj.PDGCode != 13 && isAMuon) {
3829  std::cout<<"T"<<tj.ID<<" changing PDGCode from "<<tj.PDGCode<<" to 13. Is this wise?\n";
3830  }
3831  if(isAMuon) tj.PDGCode = 13;
3832 
3833  } // SetPDGCode
3834 /*
3836  void AnalyzeHits(TCSlice& slc)
3837  {
3838  // Analyze the hits in this slice and possibly tag them hiMult
3839 
3840  bool first = true;
3841  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
3842  for(unsigned int wire = slc.firstWire[plane]; wire < slc.lastWire[plane]; ++wire) {
3843  if(slc.wireHitRange[plane][wire].first < 0) continue;
3844  unsigned int firstHit = (unsigned int)slc.wireHitRange[plane][wire].first;
3845  unsigned int lastHit = (unsigned int)slc.wireHitRange[plane][wire].second;
3846  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
3847  auto& ihit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
3848  unsigned short multCnt = 0;
3849  float rms = ihit.RMS();
3850  float time = ihit.PeakTime();
3851  bool prt = first && ihit.WireID().TPC == 2 && ihit.WireID().Plane == 2 && ihit.PeakTime() > 1100 && ihit.PeakTime() < 1400;
3852  if(prt) {
3853  std::cout<<"chk "<<(int)ihit.PeakTime()<<" iht "<<iht<<"\n";
3854  first = false;
3855  }
3856  for(unsigned int jht = iht + 1; jht < lastHit; ++jht) {
3857  auto& jhit = (*evt.allHits)[slc.slHits[jht].allHitsIndex];
3858  if(jhit.RMS() > rms) rms = jhit.RMS();
3859  float hitSep = tcc.multHitSep * rms;
3860  if(prt) std::cout<<" jht "<<jht<<" "<<(int)jhit.PeakTime()<<" rms "<<rms<<" hitSep "<<hitSep<<"\n";
3861  // break out if the next hit isn't close
3862  if(jhit.PeakTime() - time > hitSep) break;
3863  ++multCnt;
3864  time = jhit.PeakTime();
3865  } // jht
3866  if(multCnt > 8) {
3867  for(unsigned int jht = iht; jht <= iht + multCnt; ++jht) slc.slHits[jht].hiMult = true;
3868  std::cout<<"hiMult "<<PrintHit(slc.slHits[iht])<<" "<<multCnt<<"\n";
3869  } // high multiplicity
3870  iht += multCnt;
3871  } // iht
3872  } // wire
3873  } // plane
3874  } // AnalyzeHits
3875 */
3878  {
3879  // Find the average hit rms by analyzing the full hit collection. This
3880  // only needs to be done once per job.
3881 
3882  if((*evt.allHits).empty()) return false;
3883  // no sense re-calculating it if it's been done
3884  if(evt.aveHitRMSValid) return true;
3885 
3886  unsigned short cstat = (*evt.allHits)[0].WireID().Cryostat;
3887  unsigned short tpc = (*evt.allHits)[0].WireID().TPC;
3888 
3889  unsigned short nplanes = tcc.geom->Nplanes(tpc, cstat);
3890  evt.aveHitRMS.resize(nplanes);
3891  std::vector<float> cnt(nplanes, 0);
3892  for(unsigned short iht = 0; iht < (*evt.allHits).size(); ++iht) {
3893  auto& hit = (*evt.allHits)[iht];
3894  unsigned short plane = hit.WireID().Plane;
3895  if(plane > nplanes - 1) {
3896  std::cout<<"AnalyzeHits found bad plane\n";
3897  return false;
3898  } // plane check
3899  if(cnt[plane] > 200) continue;
3900  // require multiplicity one
3901  if(hit.Multiplicity() != 1) continue;
3902  // not-crazy Chisq/DOF
3903  if(hit.GoodnessOfFit() < 0 || hit.GoodnessOfFit() > 500) continue;
3904  // don't let a lot of runt hits screw up the calculation
3905  if(hit.PeakAmplitude() < 1) continue;
3906  evt.aveHitRMS[plane] += hit.RMS();
3907  ++cnt[plane];
3908  // quit if enough hits are found
3909  bool allDone = true;
3910  for(unsigned short plane = 0; plane < nplanes; ++plane) if(cnt[plane] < 200) allDone = false;
3911  if(allDone) break;
3912  } // iht
3913 
3914  // assume there are enough hits in each plane
3915  evt.aveHitRMSValid = true;
3916  for(unsigned short plane = 0; plane < nplanes; ++plane) {
3917  if(cnt[plane] > 4) {
3918  evt.aveHitRMS[plane] /= cnt[plane];
3919  } else {
3920  evt.aveHitRMS[plane] = 10;
3921  evt.aveHitRMSValid = false;
3922  } // cnt too low
3923  } // plane
3924 
3925  if(tcc.modes[kDebug]) {
3926  std::cout<<"Analyze hits aveHitRMS";
3927  std::cout<<std::fixed<<std::setprecision(1);
3928  for(auto rms : evt.aveHitRMS) std::cout<<" "<<rms;
3929  std::cout<<" aveHitRMSValid? "<<evt.aveHitRMSValid<<"\n";
3930  }
3931 
3932  return true;
3933  } // Analyze hits
3934 
3937  {
3938  // return true if the hit is in a long pulse indicating that it's position
3939  // and charge are not well known
3940  return ((hit.GoodnessOfFit() < 0 || hit.GoodnessOfFit() > 50) && hit.Multiplicity() > 5);
3941  }
3942 
3945  {
3946  // fills the WireHitRange vector. Slightly modified version of the one in ClusterCrawlerAlg.
3947  // Returns false if there was a serious error
3948 
3949  // determine the number of planes
3950  unsigned int cstat = slc.TPCID.Cryostat;
3951  unsigned int tpc = slc.TPCID.TPC;
3952  unsigned short nplanes = tcc.geom->Nplanes(tpc, cstat);
3953  slc.nPlanes = nplanes;
3954  if(nplanes > 3) {
3955  std::cout<<"FillWireHitRange: crazy nPlanes "<<nplanes<<"\n";
3956  return false;
3957  }
3958 
3959  // Y,Z limits of the detector
3960  double local[3] = {0.,0.,0.};
3961  double world[3] = {0.,0.,0.};
3962  const geo::TPCGeo &thetpc = tcc.geom->TPC(tpc, cstat);
3963  thetpc.LocalToWorld(local,world);
3964  // reduce the active area of the TPC by 1 cm to prevent wire boundary issues
3965  slc.xLo = world[0]-tcc.geom->DetHalfWidth(tpc,cstat) + 1;
3966  slc.xHi = world[0]+tcc.geom->DetHalfWidth(tpc,cstat) - 1;
3967  slc.yLo = world[1]-tcc.geom->DetHalfHeight(tpc,cstat) + 1;
3968  slc.yHi = world[1]+tcc.geom->DetHalfHeight(tpc,cstat) - 1;
3969  slc.zLo = world[2]-tcc.geom->DetLength(tpc,cstat)/2 + 1;
3970  slc.zHi = world[2]+tcc.geom->DetLength(tpc,cstat)/2 - 1;
3971 
3972  lariov::ChannelStatusProvider const& channelStatus = art::ServiceHandle<lariov::ChannelStatusService>()->GetProvider();
3973 
3974  // initialize everything
3975  slc.wireHitRange.resize(nplanes);
3976  slc.firstWire.resize(nplanes);
3977  slc.lastWire.resize(nplanes);
3978  slc.nWires.resize(nplanes);
3979  tcc.maxPos0.resize(nplanes);
3980  tcc.maxPos1.resize(nplanes);
3981  evt.aveHitRMS.resize(nplanes, nplanes);
3982 
3983  std::pair<int, int> flag;
3984  flag.first = -2; flag.second = -2;
3985 
3986  // Calculate tcc.unitsPerTick, the scale factor to convert a tick into
3987  // Wire Spacing Equivalent (WSE) units where the wire spacing in this plane = 1.
3988  // Strictly speaking this factor should be calculated for each plane to handle the
3989  // case where the wire spacing is different in each plane. Deal with this later if
3990  // the approximation used here fails.
3991 
3992  raw::ChannelID_t channel = tcc.geom->PlaneWireToChannel(0, 0, (int)tpc, (int)cstat);
3993  tcc.wirePitch = tcc.geom->WirePitch(tcc.geom->View(channel));
3994  float tickToDist = tcc.detprop->DriftVelocity(tcc.detprop->Efield(),tcc.detprop->Temperature());
3995  tickToDist *= 1.e-3 * tcc.detprop->SamplingRate(); // 1e-3 is conversion of 1/us to 1/ns
3996  tcc.unitsPerTick = tickToDist / tcc.wirePitch;
3997  for(unsigned short plane = 0; plane < nplanes; ++plane) {
3998  slc.firstWire[plane] = INT_MAX;
3999  slc.lastWire[plane] = 0;
4000  slc.nWires[plane] = tcc.geom->Nwires(plane, tpc, cstat);
4001  slc.wireHitRange[plane].resize(slc.nWires[plane], flag);
4002  tcc.maxPos0[plane] = (float)slc.nWires[plane] - 0.5;
4003  tcc.maxPos1[plane] = (float)tcc.detprop->NumberTimeSamples() * tcc.unitsPerTick;
4004  }
4005 
4006  // overwrite with the "dead wires" condition
4007  flag.first = -1; flag.second = -1;
4008  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
4009  for(unsigned int wire = 0; wire < slc.nWires[ipl]; ++wire) {
4010  raw::ChannelID_t chan = tcc.geom->PlaneWireToChannel((int)ipl, (int)wire, (int)tpc, (int)cstat);
4011  if(!channelStatus.IsGood(chan)) slc.wireHitRange[ipl][wire] = flag;
4012  } // wire
4013  } // ipl
4014 
4015  unsigned int lastWire = 0, lastPlane = 0;
4016  for(unsigned int iht = 0; iht < slc.slHits.size(); ++iht) {
4017  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4018  if(hit.WireID().Cryostat != cstat) continue;
4019  if(hit.WireID().TPC != tpc) continue;
4020  unsigned short plane = hit.WireID().Plane;
4021  unsigned int wire = hit.WireID().Wire;
4022  if(wire > slc.nWires[plane] - 1) {
4023  mf::LogWarning("TC")<<"FillWireHitRange: Invalid wire number "<<wire<<" > "<<slc.nWires[plane] - 1<<" in plane "<<plane<<" Quitting";
4024  return false;
4025  } // too large wire number
4026  if(plane == lastPlane && wire < lastWire) {
4027  mf::LogWarning("TC")<<"FillWireHitRange: Hits are not in increasing wire order. Quitting ";
4028  return false;
4029  } // hits out of order
4030  lastWire = wire;
4031  lastPlane = plane;
4032  if(slc.firstWire[plane] == INT_MAX) slc.firstWire[plane] = wire;
4033  if(slc.wireHitRange[plane][wire].first < 0) slc.wireHitRange[plane][wire].first = iht;
4034  slc.wireHitRange[plane][wire].second = iht + 1;
4035  slc.lastWire[plane] = wire + 1;
4036  } // iht
4037 
4038 // if(!CheckWireHitRange(tcs)) return false;
4039 
4040  // Find the average multiplicity 1 hit RMS and calculate the expected max RMS for each range
4041  if(tcc.modes[kDebug] && (int)tpc == debug.TPC) {
4042  std::cout<<"tpc "<<tpc<<" tcc.unitsPerTick "<<std::setprecision(3)<<tcc.unitsPerTick<<"\n";
4043  std::cout<<"Active volume (";
4044  std::cout<<std::fixed<<std::setprecision(1)<<slc.xLo<<" < X < "<<slc.xHi<<") (";
4045  std::cout<<std::fixed<<std::setprecision(1)<<slc.yLo<<" < Y < "<<slc.yHi<<") (";
4046  std::cout<<std::fixed<<std::setprecision(1)<<slc.zLo<<" < Z < "<<slc.zHi<<")\n";
4047  }
4048 
4049  return true;
4050 
4051  } // FillWireHitRange
4052 /*
4054  bool CheckWireHitRange(TCSlice& slc)
4055  {
4056  // do a QC check
4057  for(unsigned short ipl = 0; ipl < slc.nPlanes; ++ipl) {
4058  for(unsigned int wire = 0; wire < slc.nWires[ipl]; ++wire) {
4059  // No hits or dead wire
4060  if(slc.wireHitRange[ipl][wire].first < 0) continue;
4061  unsigned int firstHit = slc.wireHitRange[ipl][wire].first;
4062  unsigned int lastHit = slc.wireHitRange[ipl][wire].second;
4063  if(lastHit > xxx.fHits.size()) {
4064  mf::LogWarning("TC")<<"CheckWireHitRange: Invalid lastHit "<<lastHit<<" > fHits.size "<<xxx.fHits.size()<<" in plane "<<ipl;
4065  return false;
4066  }
4067  for(unsigned int iht = firstHit; iht < lastHit; ++iht) {
4068  if(xxx.fHits[iht].ArtPtr->WireID().Plane != ipl) {
4069  mf::LogWarning("TC")<<"CheckWireHitRange: Invalid plane "<<xxx.fHits[iht].ArtPtr->WireID().Plane<<" != "<<ipl;
4070  return false;
4071  }
4072  if(xxx.fHits[iht].ArtPtr->WireID().Wire != wire) {
4073  mf::LogWarning("TC")<<"CheckWireHitRange: Invalid wire "<<xxx.fHits[iht].ArtPtr->WireID().Wire<<" != "<<wire<<" in plane "<<ipl;
4074  return false;
4075  }
4076  } // iht
4077  } // wire
4078  } // ipl
4079 
4080  return true;
4081 
4082  } // CheckWireHitRange
4083 */
4085  bool WireHitRangeOK(TCSlice& slc, const CTP_t& inCTP)
4086  {
4087  // returns true if the passed CTP code is consistent with the CT code of the WireHitRangeVector
4088  geo::PlaneID planeID = DecodeCTP(inCTP);
4089  if(planeID.Cryostat != slc.TPCID.Cryostat) return false;
4090  if(planeID.TPC != slc.TPCID.TPC) return false;
4091  return true;
4092  }
4093 
4095  bool MergeAndStore(TCSlice& slc, unsigned int itj1, unsigned int itj2, bool doPrt)
4096  {
4097  // Merge the two trajectories in allTraj and store them. Returns true if it was successfull.
4098  // Merging is done between the end (end = 1) of tj1 and the beginning (end = 0) of tj2. This function preserves the
4099  // AlgMod state of itj1.
4100  // The itj1 -> itj2 merge order is reversed if end1 of itj2 is closer to end0 of itj1
4101 
4102  if(itj1 > slc.tjs.size() - 1) return false;
4103  if(itj2 > slc.tjs.size() - 1) return false;
4104  if(slc.tjs[itj1].AlgMod[kKilled] || slc.tjs[itj2].AlgMod[kKilled]) return false;
4105  if(slc.tjs[itj1].AlgMod[kHaloTj] || slc.tjs[itj2].AlgMod[kHaloTj]) return false;
4106 
4107  // Merging shower Tjs requires merging the showers as well.
4108  if(slc.tjs[itj1].AlgMod[kShowerTj] || slc.tjs[itj2].AlgMod[kShowerTj]) return MergeShowerTjsAndStore(slc, itj1, itj2, doPrt);
4109 
4110  // Ensure that the order of 3D-matched Tjs is consistent with the convention that
4111  unsigned short pfp1 = GetPFPIndex(slc, slc.tjs[itj1].ID);
4112  unsigned short pfp2 = GetPFPIndex(slc, slc.tjs[itj2].ID);
4113  if(pfp1 != USHRT_MAX || pfp2 != USHRT_MAX) {
4114  if(pfp1 != USHRT_MAX && pfp2 != USHRT_MAX) {
4115 // std::cout<<"MAS: Both tjs are used in a PFParticle. Need PFParticle merging code to do this. pfps size "<<slc.pfps.size()<<"\n";
4116  return false;
4117  }
4118  // Swap so that the order of tj1 is preserved. Tj2 may be reversed to be consistent
4119  if(pfp1 == USHRT_MAX) std::swap(itj1, itj2);
4120  } // one or both used in a PFParticle
4121 
4122  // make copies so they can be trimmed as needed
4123  Trajectory tj1 = slc.tjs[itj1];
4124  Trajectory tj2 = slc.tjs[itj2];
4125 
4126  // ensure that these are in the same step order
4127  if(tj2.StepDir != tj1.StepDir) ReverseTraj(slc, tj2);
4128 
4129  Point2_t tp1e0 = tj1.Pts[tj1.EndPt[0]].Pos;
4130  Point2_t tp1e1 = tj1.Pts[tj1.EndPt[1]].Pos;
4131  Point2_t tp2e0 = tj2.Pts[tj2.EndPt[0]].Pos;
4132  Point2_t tp2e1 = tj2.Pts[tj2.EndPt[1]].Pos;
4133 
4134  if(doPrt) {
4135  mf::LogVerbatim("TC")<<"MergeAndStore: tj1 T"<<tj1.ID<<" tj2 T"<<tj2.ID<<" at merge points "<<PrintPos(slc, tp1e1)<<" "<<PrintPos(slc, tp2e0);
4136  }
4137 
4138  // swap the order so that abs(tj1end1 - tj2end0) is less than abs(tj2end1 - tj1end0)
4139  if(PosSep2(tp1e1, tp2e0) > PosSep2(tp2e1, tp1e0)) {
4140  std::swap(tj1, tj2);
4141  std::swap(tp1e0, tp2e0);
4142  std::swap(tp1e1, tp2e1);
4143  if(doPrt) mf::LogVerbatim("TC")<<" swapped the order. Merge points "<<PrintPos(slc, tp1e1)<<" "<<PrintPos(slc, tp2e0);
4144  }
4145 
4146  // Here is what we are looking for, where - indicates a TP with charge.
4147  // Note that this graphic is in the stepping direction (+1 = +wire direction)
4148  // tj1: 0------------1
4149  // tj2: 0-----------1
4150  // Another possibility with overlap
4151  // tj1: 0-------------1
4152  // tj2: 0--------------1
4153 
4154  if(tj1.StepDir > 1) {
4155  // Not allowed
4156  // tj1: 0---------------------------1
4157  // tj2: 0------1
4158  if(tp2e0[0] > tp1e0[0] && tp2e1[0] < tp1e1[0]) return false;
4160  // tj1: 0------1
4161  // tj2: 0---------------------------1
4162  if(tp1e0[0] > tp2e0[0] && tp1e1[0] < tp2e1[0]) return false;
4163  } else {
4164  // same as above but with ends reversed
4165  if(tp2e1[0] > tp1e1[0] && tp2e0[0] < tp1e0[0]) return false;
4166  if(tp1e1[0] > tp2e1[0] && tp1e0[0] < tp2e0[0]) return false;
4167  }
4168 
4169  if(tj1.VtxID[1] > 0 && tj2.VtxID[0] == tj1.VtxID[1]) {
4170  auto& vx = slc.vtxs[tj1.VtxID[1] - 1];
4171  if(!MakeVertexObsolete("MAS", slc, vx, false)) {
4172  if(doPrt) mf::LogVerbatim("TC")<<"MergeAndStore: Found a good vertex between Tjs "<<tj1.VtxID[1]<<" No merging";
4173  return false;
4174  }
4175  }
4176 
4177  if(tj1.StopFlag[1][kBragg]) {
4178  if(doPrt) mf::LogVerbatim("TC")<<"MergeAndStore: You are merging the end of trajectory T"<<tj1.ID<<" with a Bragg peak. Not merging\n";
4179  return false;
4180  }
4181 
4182  // remove any points at the end of tj1 that don't have used hits
4183  tj1.Pts.resize(tj1.EndPt[1] + 1);
4184 
4185  // determine if they overlap by finding the point on tj2 that is closest
4186  // to the end point of tj1.
4187  TrajPoint& endtj1TP = tj1.Pts[tj1.EndPt[1]];
4188  // Set minSep large so that dead wire regions are accounted for
4189  float minSep = 1000;
4190  unsigned short tj2ClosePt = 0;
4191  // Note that TrajPointTrajDOCA only considers TPs that have charge
4192  TrajPointTrajDOCA(slc, endtj1TP, tj2, tj2ClosePt, minSep);
4193  if(doPrt) mf::LogVerbatim("TC")<<" Merge point tj1 "<<PrintPos(slc, endtj1TP)<<" tj2ClosePt "<<tj2ClosePt<<" Pos "<<PrintPos(slc, tj2.Pts[tj2ClosePt]);
4194  // check for full overlap
4195  if(tj2ClosePt > tj2.EndPt[1]) return false;
4196 
4197  // The approach is to append tj2 to tj1, store tj1 as a new trajectory,
4198  // and re-assign all hits to the new trajectory
4199 
4200  // First ensure that any hit will appear only once in the merged trajectory in the overlap region
4201  // whether it is used or unused. The point on tj2 where the merge will begin, tj2ClosePt, will be
4202  // increased until this condition is met.
4203  // Make a temporary vector of tj1 hits in the end points for simpler searching
4204  std::vector<unsigned int> tj1Hits;
4205  for(unsigned short ii = 0; ii < tj1.Pts.size(); ++ii) {
4206  // only go back a few points in tj1
4207  if(ii > 10) break;
4208  unsigned short ipt = tj1.Pts.size() - 1 - ii;
4209  tj1Hits.insert(tj1Hits.end(), tj1.Pts[ipt].Hits.begin(), tj1.Pts[ipt].Hits.end());
4210  if(ipt == 0) break;
4211  } // ii
4212 
4213  bool bumpedPt = true;
4214  while(bumpedPt) {
4215  bumpedPt = false;
4216  for(unsigned short ii = 0; ii < tj2.Pts[tj2ClosePt].Hits.size(); ++ii) {
4217  unsigned int iht = tj2.Pts[tj2ClosePt].Hits[ii];
4218  if(std::find(tj1Hits.begin(), tj1Hits.end(), iht) != tj1Hits.end()) bumpedPt = true;
4219  } // ii
4220  if(bumpedPt && tj2ClosePt < tj2.EndPt[1]) {
4221  ++tj2ClosePt;
4222  } else {
4223  break;
4224  }
4225  } // bumpedPt
4226  if(doPrt) mf::LogVerbatim("TC")<<" revised tj2ClosePt "<<tj2ClosePt;
4227  // append tj2 hits to tj1
4228 
4229  tj1.Pts.insert(tj1.Pts.end(), tj2.Pts.begin() + tj2ClosePt, tj2.Pts.end());
4230  // re-define the end points
4231  SetEndPoints(tj1);
4232  tj1.StopFlag[1] = tj2.StopFlag[1];
4233 
4234  // A more exhaustive check that hits only appear once
4235  if(HasDuplicateHits(slc, tj1, doPrt)) {
4236  if(doPrt) {
4237  mf::LogVerbatim("TC")<<"MergeAndStore found duplicate hits. Coding error";
4238  PrintTrajectory("MAS", slc, tj1, USHRT_MAX);
4239  PrintTrajectory("tj1", slc, slc.tjs[itj1], USHRT_MAX);
4240  PrintTrajectory("tj2", slc, slc.tjs[itj2], USHRT_MAX);
4241  }
4242  return false;
4243  }
4244  if(tj2.VtxID[1] > 0) {
4245  // move the end vertex of tj2 to the end of tj1
4246  tj1.VtxID[1] = tj2.VtxID[1];
4247  }
4248  // Transfer some of the AlgMod bits
4249  if(tj2.AlgMod[kMichel]) tj1.AlgMod[kMichel] = true;
4250  if(tj2.AlgMod[kDeltaRay]) {
4251  tj1.AlgMod[kDeltaRay] = true;
4252  tj1.ParentID = tj2.ParentID;
4253  }
4254  // keep track of the IDs before they are clobbered
4255  int tj1ID = tj1.ID;
4256  int tj2ID = tj2.ID;
4257  // kill the original trajectories
4258  MakeTrajectoryObsolete(slc, itj1);
4259  MakeTrajectoryObsolete(slc, itj2);
4260  // Do this so that StoreTraj keeps the correct WorkID (of itj1)
4261  tj1.ID = tj1.WorkID;
4262  SetPDGCode(slc, tj1, true);
4263  if(!StoreTraj(slc, tj1)) return false;
4264  int newTjID = slc.tjs.size();
4265  // Use the ParentID to trace which new Tj is superseding the merged ones
4266  tj1.ParentID = newTjID;
4267  tj2.ParentID = newTjID;
4268  // update match structs if they exist
4269  UpdateMatchStructs(slc, tj1.ID, newTjID);
4270  UpdateMatchStructs(slc, tj2.ID, newTjID);
4271  if(doPrt) mf::LogVerbatim("TC")<<" MAS success. Created T"<<newTjID;
4272  // Transfer the ParentIDs of any other Tjs that refer to Tj1 and Tj2 to the new Tj
4273  for(auto& tj : slc.tjs) if(tj.ParentID == tj1ID || tj.ParentID == tj2ID) tj.ParentID = newTjID;
4274 
4275  return true;
4276  } // MergeAndStore
4277 
4279  std::vector<int> GetAssns(TCSlice& slc, std::string type1Name, int id, std::string type2Name)
4280  {
4281  // returns a list of IDs of objects (slc, vertices, pfps, etc) with type1Name that are in slc with
4282  // type2Name. This is intended to be a general purpose replacement for specific functions like GetVtxTjIDs, etc
4283 
4284  std::vector<int> tmp;
4285  if(id <= 0) return tmp;
4286  unsigned int uid = id;
4287 
4288  if(type1Name == "T" && uid <= slc.tjs.size() && type2Name == "P") {
4289  // return a list of PFPs that have the tj in TjIDs, P -> T<ID>
4290  for(auto& pfp : slc.pfps) {
4291  if(pfp.ID <= 0) continue;
4292  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), id) != pfp.TjIDs.end()) tmp.push_back(pfp.ID);
4293  } // pf
4294  return tmp;
4295  } // P -> T
4296 
4297  if(type1Name == "P" && uid <= slc.pfps.size() && (type2Name == "2S" || type2Name == "3S")) {
4298  // return a list of 3D or 2D showers with the assn 3S -> 2S -> T -> P<ID> or 2S -> T -> P.
4299  auto& pfp = slc.pfps[uid - 1];
4300  // First form a list of 2S -> T -> P<ID>
4301  std::vector<int> ssid;
4302  for(auto& ss : slc.cots) {
4303  if(ss.ID <= 0) continue;
4304  auto shared = SetIntersection(ss.TjIDs, pfp.TjIDs);
4305  if(!shared.empty() && std::find(ssid.begin(), ssid.end(), ss.ID) == ssid.end()) ssid.push_back(ss.ID);
4306  } // ss
4307  if(type2Name == "2S") return ssid;
4308  for(auto& ss3 : slc.showers) {
4309  if(ss3.ID <= 0) continue;
4310  auto shared = SetIntersection(ss3.CotIDs, ssid);
4311  if(!shared.empty() && std::find(tmp.begin(), tmp.end(), ss3.ID) == tmp.end()) tmp.push_back(ss3.ID);
4312  } // ss3
4313  return tmp;
4314  } // 3S -> 2S -> T -> P
4315 
4316  if(type1Name == "2V" && uid <= slc.vtxs.size() && type2Name == "T" ) {
4317  // 2V -> T
4318  for(auto& tj : slc.tjs) {
4319  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4320  for(unsigned short end = 0; end < 2; ++end) {
4321  if(tj.VtxID[end] != id) continue;
4322  if(std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4323  } // end
4324  } // tj
4325  return tmp;
4326  } // 2V -> T
4327 
4328  if(type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "P") {
4329  for(auto& pfp : slc.pfps) {
4330  if(pfp.ID == 0) continue;
4331  for(unsigned short end = 0; end < 2; ++end) {
4332  if(pfp.Vx3ID[end] != id) continue;
4333  // encode the end with the ID
4334  if(std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4335  } // end
4336  } // pfp
4337  return tmp;
4338  } // 3V -> P
4339 
4340  if(type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "T") {
4341  // 3V -> T
4342  for(auto& tj : slc.tjs) {
4343  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4344  for(unsigned short end = 0; end < 2; ++end) {
4345  if(tj.VtxID[end] > 0 && tj.VtxID[end] <= slc.vtxs.size()) {
4346  auto& vx2 = slc.vtxs[tj.VtxID[end] - 1];
4347  if(vx2.Vx3ID != id) continue;
4348  if(std::find(tmp.begin(), tmp.end(), tj.ID) == tmp.end()) tmp.push_back(tj.ID);
4349  }
4350  } // end
4351  } // tj
4352  return tmp;
4353  } // 3V -> T
4354 
4355  if(type1Name == "3V" && uid <= slc.vtx3s.size() && type2Name == "2V") {
4356  // 3V -> 2V
4357  for(auto& vx2 : slc.vtxs) {
4358  if(vx2.ID == 0) continue;
4359  if(vx2.Vx3ID == id) tmp.push_back(vx2.ID);
4360  } // vx2
4361  return tmp;
4362  } // 3V -> 2V
4363 
4364  if(type1Name == "3S" && uid <= slc.showers.size() && type2Name == "T") {
4365  // 3S -> T
4366  auto& ss3 = slc.showers[uid - 1];
4367  if(ss3.ID == 0) return tmp;
4368  for(auto cid : ss3.CotIDs) {
4369  auto& ss = slc.cots[cid - 1];
4370  if(ss.ID == 0) continue;
4371  tmp.insert(tmp.end(), ss.TjIDs.begin(), ss.TjIDs.end());
4372  } // cid
4373  return tmp;
4374  } // 3S -> T
4375 
4376  // This isn't strictly necessary but do it for consistency
4377  if(type1Name == "2S" && uid <= slc.cots.size() && type2Name == "T") {
4378  // 2S -> T
4379  auto& ss = slc.cots[uid - 1];
4380  return ss.TjIDs;
4381  } // 2S -> T
4382 
4383  if(type1Name == "3S" && uid <= slc.showers.size() && type2Name == "P") {
4384  // 3S -> P
4385  auto& ss3 = slc.showers[uid - 1];
4386  if(ss3.ID == 0) return tmp;
4387  for(auto cid : ss3.CotIDs) {
4388  auto& ss = slc.cots[cid - 1];
4389  if(ss.ID == 0) continue;
4390  for(auto tid : ss.TjIDs) {
4391  auto& tj = slc.tjs[tid - 1];
4392  if(tj.AlgMod[kKilled] || tj.AlgMod[kHaloTj]) continue;
4393  if(!tj.AlgMod[kMat3D]) continue;
4394  for(auto& pfp : slc.pfps) {
4395  if(pfp.ID <= 0) continue;
4396  if(std::find(pfp.TjIDs.begin(), pfp.TjIDs.end(), tj.ID) == pfp.TjIDs.end()) continue;
4397  if(std::find(tmp.begin(), tmp.end(), pfp.ID) == tmp.end()) tmp.push_back(pfp.ID);
4398  } // pf
4399  } // tid
4400  } // cid
4401  return tmp;
4402  } // 3S -> P
4403 
4404  if(type1Name == "T" && uid <= slc.tjs.size() && type2Name == "2S") {
4405  // T -> 2S
4406  for(auto& ss : slc.cots) {
4407  if(ss.ID == 0) continue;
4408  if(std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) != ss.TjIDs.end()) tmp.push_back(ss.ID);
4409  } // ss
4410  return tmp;
4411  } // T -> 2S
4412 
4413  if(type1Name == "T" && uid <= slc.tjs.size() && type2Name == "3S") {
4414  // T -> 3S
4415  for(auto& ss : slc.cots) {
4416  if(ss.ID == 0) continue;
4417  if(std::find(ss.TjIDs.begin(), ss.TjIDs.end(), id) == ss.TjIDs.end()) continue;
4418  if(ss.SS3ID > 0) tmp.push_back(ss.SS3ID);
4419  } // ss
4420  return tmp;
4421  } // T -> 3S
4422 
4423  std::cout<<"GetAssns doesn't know about "<<type1Name<<" -> "<<type2Name<<" assns, or id "<<id<<" is not valid.\n";
4424 
4425  return tmp;
4426 
4427  } // GetAssns
4428 
4429 
4431  bool StartTraj(TCSlice& slc, Trajectory& tj, unsigned int fromhit, unsigned int tohit, unsigned short pass)
4432  {
4433  // Start a trajectory located at fromHit with direction pointing to toHit
4434 
4435  auto& fromHit = (*evt.allHits)[slc.slHits[fromhit].allHitsIndex];
4436  auto& toHit = (*evt.allHits)[slc.slHits[tohit].allHitsIndex];
4437  float fromWire = fromHit.WireID().Wire;
4438  float fromTick = fromHit.PeakTime();
4439  float toWire = toHit.WireID().Wire;
4440  float toTick = toHit.PeakTime();
4441  CTP_t tCTP = EncodeCTP(fromHit.WireID());
4442  bool success = StartTraj(slc, tj, fromWire, fromTick, toWire, toTick, tCTP, pass);
4443  if(!success) return false;
4444  // turn on debugging using the WorkID?
4445  if(tcc.modes[kDebug] && !tcc.dbgStp && !tcc.dbgDump && tcc.dbgSlc && tj.ID == debug.WorkID) tcc.dbgStp = true;
4446  if(tcc.dbgStp) {
4447  auto& tp = tj.Pts[0];
4448  mf::LogVerbatim("TC")<<"StartTraj T"<<tj.ID<<" from "<<(int)fromWire<<":"<<(int)fromTick<<" -> "<<(int)toWire<<":"<<(int)toTick<<" StepDir "<<tj.StepDir<<" dir "<<tp.Dir[0]<<" "<<tp.Dir[1]<<" ang "<<tp.Ang<<" AngleCode "<<tp.AngleCode<<" angErr "<<tp.AngErr<<" ExpectedHitsRMS "<<ExpectedHitsRMS(slc, tp);
4449  } // tcc.dbgStp
4450  return true;
4451  } // StartTraj
4452 
4454  bool StartTraj(TCSlice& slc, Trajectory& tj, float fromWire, float fromTick, float toWire, float toTick, CTP_t& tCTP, unsigned short pass)
4455  {
4456  // Start a simple (seed) trajectory going from (fromWire, toTick) to (toWire, toTick).
4457 
4458  // decrement the work ID so we can use it for debugging problems
4459  --evt.WorkID;
4460  if(evt.WorkID == INT_MIN) evt.WorkID = -1;
4461  tj.ID = evt.WorkID;
4462  tj.Pass = pass;
4463  // Assume we are stepping in the positive WSE units direction
4464  short stepdir = 1;
4465  int fWire = std::nearbyint(fromWire);
4466  int tWire = std::nearbyint(toWire);
4467  if(tWire < fWire) {
4468  stepdir = -1;
4469  } else if(tWire == fWire) {
4470  // on the same wire
4471  if(toTick < fromTick) stepdir = -1;
4472  }
4473  tj.StepDir = stepdir;
4474  tj.CTP = tCTP;
4475  tj.ParentID = -1;
4476  tj.Strategy.reset();
4477  tj.Strategy[kNormal] = true;
4478 
4479  // create a trajectory point
4480  TrajPoint tp;
4481  if(!MakeBareTrajPoint(slc, fromWire, fromTick, toWire, toTick, tCTP, tp)) return false;
4482  SetAngleCode(tp);
4483  tp.AngErr = 0.1;
4484  tj.Pts.push_back(tp);
4485  // turn on debugging using the WorkID?
4486  if(tcc.modes[kDebug] && !tcc.dbgStp && !tcc.dbgDump && tcc.dbgSlc && tj.ID == debug.WorkID) tcc.dbgStp = true;
4487  if(tcc.dbgStp) {
4488  auto& tp = tj.Pts[0];
4489  mf::LogVerbatim("TC")<<"StartTraj T"<<tj.ID<<" from "<<(int)fromWire<<":"<<(int)fromTick<<" -> "<<(int)toWire<<":"<<(int)toTick<<" StepDir "<<tj.StepDir<<" dir "<<tp.Dir[0]<<" "<<tp.Dir[1]<<" ang "<<tp.Ang<<" AngleCode "<<tp.AngleCode<<" angErr "<<tp.AngErr<<" ExpectedHitsRMS "<<ExpectedHitsRMS(slc, tp);
4490  } // tcc.dbgStp
4491  return true;
4492 
4493  } // StartTraj
4494 
4496  std::pair<unsigned short, unsigned short> GetSliceIndex(std::string typeName, int uID)
4497  {
4498  // returns the slice index and product index of a data product having typeName and unique ID uID
4499  for(unsigned short isl = 0; isl < slices.size(); ++isl) {
4500  auto& slc = slices[isl];
4501  if(typeName == "T") {
4502  for(unsigned short indx = 0; indx < slc.tjs.size(); ++indx) {
4503  if(slc.tjs[indx].UID == uID) { return std::make_pair(isl, indx); }
4504  }
4505  } // T
4506  if(typeName == "P") {
4507  for(unsigned short indx = 0; indx < slc.pfps.size(); ++indx) {
4508  if(slc.pfps[indx].UID == uID) { return std::make_pair(isl, indx); }
4509  }
4510  } // P
4511  if(typeName == "2V") {
4512  for(unsigned short indx = 0; indx < slc.vtxs.size(); ++indx) {
4513  if(slc.vtxs[indx].UID == uID) { return std::make_pair(isl, indx); }
4514  }
4515  } // 2V
4516  if(typeName == "3V") {
4517  for(unsigned short indx = 0; indx < slc.vtx3s.size(); ++indx) {
4518  if(slc.vtx3s[indx].UID == uID) { return std::make_pair(isl, indx); }
4519  }
4520  } // 3V
4521  if(typeName == "2S") {
4522  for(unsigned short indx = 0; indx < slc.cots.size(); ++indx) {
4523  if(slc.cots[indx].UID == uID) { return std::make_pair(isl, indx); }
4524  }
4525  } // 2S
4526  if(typeName == "3S") {
4527  for(unsigned short indx = 0; indx < slc.showers.size(); ++indx) {
4528  if(slc.showers[indx].UID == uID) { return std::make_pair(isl, indx); }
4529  }
4530  } // T
4531  } // isl
4532  return std::make_pair(USHRT_MAX, USHRT_MAX);
4533  } // GetSliceIndex
4534 
4536  bool Fit2D(short mode, Point2_t inPt, float& inPtErr, Vector2_t& outVec, Vector2_t& outVecErr, float& chiDOF)
4537  {
4538  // Fit points to a 2D line.
4539  // Mode = 0: Initialize
4540  // Mode = 1: Accumulate
4541  // Mode = 2: Accumulate and store to calculate chiDOF
4542  // Mode = -1: Fit and put results in outVec and chiDOF
4543 
4544  static double sum, sumx, sumy, sumx2, sumy2, sumxy;
4545  static unsigned short cnt;
4546  static std::vector<Point2_t> fitPts;
4547  static std::vector<double> fitWghts;
4548 
4549  if(mode == 0) {
4550  // initialize
4551  cnt = 0;
4552  sum = 0.; sumx = 0.; sumy = 0.;
4553  sumx2 = 0.; sumy2 = 0.; sumxy = 0;
4554  fitPts.resize(0);
4555  fitWghts.resize(0);
4556  return true;
4557  } // mode == 0
4558 
4559  if(mode > 0) {
4560  if(inPtErr <= 0.) return false;
4561  ++cnt;
4562  double wght = 1 / (inPtErr * inPtErr);
4563  sum += wght;
4564  sumx += wght * inPt[0];
4565  sumx2 += wght * inPt[0] * inPt[0];
4566  sumy += wght * inPt[1];
4567  sumy2 += wght * inPt[1] * inPt[1];
4568  sumxy += wght * inPt[0] * inPt[1];
4569  if(mode == 1) return true;
4570  fitPts.push_back(inPt);
4571  fitWghts.push_back(wght);
4572  return true;
4573  } // Accumulate
4574 
4575  if(cnt < 2) return false;
4576  // do the fit
4577  double delta = sum * sumx2 - sumx * sumx;
4578  if(delta == 0.) return false;
4579  double A = (sumx2 * sumy - sumx * sumxy) / delta;
4580  double B = (sumxy * sum - sumx * sumy) / delta;
4581  outVec[0] = A;
4582  outVec[1] = B;
4583  chiDOF = 0;
4584  if(cnt == 2 || fitPts.empty()) return true;
4585 
4586  // calculate errors and chiDOF
4587  if(fitPts.size() != cnt) return false;
4588  double ndof = cnt - 2;
4589  double varnce = (sumy2 + A*A*sum + B*B*sumx2 - 2 * (A*sumy + B*sumxy - A*B*sumx)) / ndof;
4590  if(varnce > 0.) {
4591  outVecErr[0] = sqrt(varnce * sumx2 / delta);
4592  outVecErr[1] = sqrt(varnce * sum / delta);
4593  } else {
4594  outVecErr[0] = 0.;
4595  outVecErr[1] = 0.;
4596  }
4597  sum = 0.;
4598  // calculate chisq
4599  for(unsigned short ii = 0; ii < fitPts.size(); ++ii) {
4600  double arg = fitPts[ii][1] - A - B * fitPts[ii][0];
4601  sum += fitWghts[ii] * arg * arg;
4602  }
4603  chiDOF = sum / ndof;
4604  fitPts.resize(0);
4605  fitWghts.resize(0);
4606  return true;
4607 
4608  } // Fit2D
4609 
4611  bool DecodeDebugString(std::string strng)
4612  {
4613  // try to unpack the string as Cryostat:TPC:Plane:Wire:Tick or something
4614  // like Slice:<slice index>
4615 
4616  if(strng == "instruct") {
4617  std::cout<<"****** Unrecognized DebugConfig. Here are your options\n";
4618  std::cout<<" 'C:T:P:W:Tick' where C = cryostat, T = TPC, W = wire, Tick (+/-5) to debug stepping (DUNE)\n";
4619  std::cout<<" 'P:W:Tick' for single cryostat/TPC detectors (uB, LArIAT, etc)\n";
4620  std::cout<<" 'WorkID <id> <slice index>' where <id> is a tj work ID (< 0) in slice <slice index> (default = 0)\n";
4621  std::cout<<" 'Merge <CTP>' to debug trajectory merging\n";
4622  std::cout<<" '2V <plane>' to debug 2D vertex finding in plane <plane>\n";
4623  std::cout<<" '3V' to debug 3D vertex finding\n";
4624  std::cout<<" 'VxMerge' to debug 2D vertex merging\n";
4625  std::cout<<" 'JunkVx' to debug 2D junk vertex finder\n";
4626  std::cout<<" 'PFP' to debug 3D matching and PFParticles\n";
4627  std::cout<<" 'DeltaRay' to debug delta ray tagging\n";
4628  std::cout<<" 'Muon' to debug muon tagging\n";
4629  std::cout<<" '2S <plane>' to debug a 2D shower in plane <plane>\n";
4630  std::cout<<" 'Reco <ID>' to reconstruct all sub-slices in the recob::Slice with the specified ID\n";
4631  std::cout<<" 'SubSlice <sub-slice index>' where <slice index> restricts output to the specified sub-slice index\n";
4632  std::cout<<" 'Stitch' to debug PFParticle stitching between TPCs\n";
4633  std::cout<<" 'Sum' or 'Summary' to print a debug summary report\n";
4634  std::cout<<" 'Dump <WorkID>' or 'Dump <UID>' to print all TPs in the trajectory to tcdump<UID>.csv\n";
4635  std::cout<<" Note: Algs with debug printing include HamVx, HamVx2, SplitTjCVx, Comp3DVx, Comp3DVxIG, VtxHitsSwap\n";
4636  std::cout<<" Set SkipAlgs: [\"bogusText\"] to print a list of algorithm names\n";
4637  return false;
4638  } // instruct
4639 
4640  // handle the simple cases that don't need decoding
4641  if(strng.find("3V") != std::string::npos) { tcc.dbg3V = true; tcc.modes[kDebug] = true; return true; }
4642  if(strng.find("3S") != std::string::npos) { tcc.dbg3S = true; tcc.modes[kDebug] = true; return true; }
4643  if(strng.find("VxMerge") != std::string::npos) { tcc.dbgVxMerge = true; tcc.modes[kDebug] = true; return true; }
4644  if(strng.find("JunkVx") != std::string::npos) { tcc.dbgVxJunk = true; tcc.modes[kDebug] = true; return true; }
4645  if(strng.find("PFP") != std::string::npos) { tcc.dbgPFP = true; tcc.modes[kDebug] = true; return true; }
4646  if(strng.find("DeltaRay") != std::string::npos) { tcc.dbgDeltaRayTag = true; tcc.modes[kDebug] = true; return true; }
4647  if(strng.find("Muon") != std::string::npos) { tcc.dbgMuonTag = true; tcc.modes[kDebug] = true; return true; }
4648  if(strng.find("Stitch") != std::string::npos) { tcc.dbgStitch = true; tcc.modes[kDebug] = true; return true; }
4649  if(strng.find("Sum") != std::string::npos) { tcc.dbgSummary = true; tcc.modes[kDebug] = true; return true; }
4650 
4651  std::vector<std::string> words;
4652  boost::split(words, strng, boost::is_any_of(" :"), boost::token_compress_on);
4653  if(words.size() == 5) {
4654  // configure for DUNE
4655  debug.Cryostat = std::stoi(words[0]);
4656  debug.TPC = std::stoi(words[1]);
4657  debug.Plane = std::stoi(words[2]);
4658  debug.Wire = std::stoi(words[3]);
4659  debug.Tick = std::stoi(words[4]);
4660  tcc.modes[kDebug] = true;
4661  tcc.dbgStp = true;
4662  // also dump this tj
4663  tcc.dbgDump = true;
4664  return true;
4665  } // nums.size() == 5
4666  if(words.size() == 2 && words[0] == "Dump") {
4667  debug.WorkID = std::stoi(words[1]);
4668  debug.Slice = 0;
4669  tcc.modes[kDebug] = true;
4670  tcc.dbgDump = true;
4671  return true;
4672  }
4673  if(words.size() > 1 && words[0] == "WorkID") {
4674  debug.WorkID = std::stoi(words[1]);
4675  if(debug.WorkID >= 0) return false;
4676  // default to sub-slice index 0
4677  debug.Slice = 0;
4678  if(words.size() > 2) debug.Slice = std::stoi(words[2]);
4679  tcc.modes[kDebug] = true;
4680  // dbgStp is set true after debug.WorkID is found
4681  tcc.dbgStp = false;
4682  return true;
4683  } // words.size() == 3 && words[0] == "WorkID"
4684  if(words.size() == 3) {
4685  // configure for uB, LArIAT, etc
4686  debug.Cryostat = 0;
4687  debug.TPC = 0;
4688  debug.Plane = std::stoi(words[0]);
4689  debug.Wire = std::stoi(words[1]);
4690  debug.Tick = std::stoi(words[2]);
4691  tcc.modes[kDebug] = true;
4692  tcc.dbgStp = true;
4693  return true;
4694  }
4695  if(words.size() == 2 && words[0] == "Merge") {
4696  debug.CTP = std::stoi(words[1]);
4697  tcc.dbgMrg = true;
4698  tcc.modes[kDebug] = true;
4699  return true;
4700  }
4701  if(words.size() == 2 && words[0] == "2V") {
4702  debug.Plane = std::stoi(words[1]);
4703  tcc.dbg2V = true;
4704  tcc.modes[kDebug] = true;
4705  return true;
4706  }
4707  if(words.size() == 2 && words[0] == "2S") {
4708  debug.Plane = std::stoi(words[1]);
4709  tcc.dbg2S = true;
4710  tcc.modes[kDebug] = true;
4711  return true;
4712  }
4713  // Slice could apply to several debug options.
4714  if(words.size() == 2 && words[0] == "SubSlice") {
4715  debug.Slice = std::stoi(words[1]);
4716  return true;
4717  }
4718  if(words.size() == 2 && words[0] == "Reco") {
4719  tcc.recoSlice = std::stoi(words[1]);
4720  std::cout<<"Reconstructing Slice "<<tcc.recoSlice<<"\n";
4721  return true;
4722  }
4723  return false;
4724  } // DecodeDebugString
4725 
4726  // ****************************** Printing ******************************
4727 
4728  void DumpTj()
4729  {
4730  // Dump all of the points in a trajectory to the output in a form that can
4731  // be imported by another application, e.g. Excel
4732  // Search for the trajectory with the specified WorkID or Unique ID
4733 
4734  for(auto& slc : slices) {
4735  for(auto& tj : slc.tjs) {
4736  if(tj.AlgMod[kKilled]) continue;
4737  if(tj.WorkID != debug.WorkID && tj.UID !=debug.WorkID) continue;
4738  // print a header
4739  std::ofstream outfile;
4740  std::string fname = "tcdump" + std::to_string(tj.UID) + ".csv";
4741  outfile.open(fname,std::ios::out | std::ios::trunc);
4742  outfile<<"Dump trajectory T"<<tj.UID<<" WorkID "<<tj.WorkID;
4743  outfile<<" ChgRMS "<<std::setprecision(2)<<tj.ChgRMS;
4744  outfile<<"\n";
4745  outfile<<"Wire, Chg T"<<tj.UID<<", totChg, Tick, Delta, NTPsFit, Ang, TPErr\n";
4746  for(unsigned short ipt = tj.EndPt[0]; ipt <= tj.EndPt[1]; ++ipt) {
4747  auto& tp = tj.Pts[ipt];
4748  outfile<<std::fixed;
4749  outfile<<std::setprecision(0)<<std::nearbyint(tp.Pos[0]);
4750  outfile<<","<<(int)tp.Chg;
4751  // total charge near the TP
4752  float totChg = 0;
4753  for(auto iht : tp.Hits) {
4754  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
4755  totChg += hit.Integral();
4756  }
4757  outfile<<","<<(int)totChg;
4758  outfile<<","<<std::setprecision(0)<<std::nearbyint(tp.Pos[1]/tcc.unitsPerTick);
4759  outfile<<","<<std::setprecision(2)<<tp.Delta;
4760  outfile<<","<<tp.NTPsFit;
4761  outfile<<","<<std::setprecision(2)<<tp.Ang;
4762  outfile<<","<<std::setprecision(2)<<sqrt(tp.HitPosErr2);
4763  outfile<<"\n";
4764  } // ipt
4765  outfile.close();
4766  std::cout<<"Points on T"<<tj.UID<<" dumped to "<<fname<<"\n";
4767  return;
4768  } // tj
4769  } // slc
4770 
4771  } // DumpTj
4772 
4774  void PrintAll(std::string someText, const std::vector<simb::MCParticle*>& mcpList)
4775  {
4776  // print everything in all slices
4777  bool prt3V = false;
4778  bool prt2V = false;
4779  bool prtT = false;
4780  bool prtP = false;
4781  bool prtS3 = false;
4782  //bool prtS2 = false;
4783  for(size_t isl = 0; isl < slices.size(); ++isl) {
4784  if(debug.Slice >= 0 && int(isl) != debug.Slice) continue;
4785  auto& slc = slices[isl];
4786  if(!slc.vtx3s.empty()) prt3V = true;
4787  if(!slc.vtxs.empty()) prt2V = true;
4788  if(!slc.tjs.empty()) prtT = true;
4789  if(!slc.pfps.empty()) prtP = true;
4790  if(!slc.showers.empty()) prtS3 = true;
4791  //if(!slc.cots.empty()) prtS2 = true;
4792  } // slc
4793  mf::LogVerbatim myprt("TC");
4794  myprt<<"Debug report from caller "<<someText<<"\n";
4795  if(!mcpList.empty()) {
4797  myprt<<"************ "<<mcpList.size()<<" MCParticles (> 10 MeV) ************\n";
4798  myprt<<" mcpindx PDG KE eveIndx Process\n";
4799  for(unsigned int imcp = 0; imcp < mcpList.size(); ++imcp) {
4800  auto& mcp = mcpList[imcp];
4801  int pdg = abs(mcp->PdgCode());
4802  if(pdg > 3000) continue;
4803  float TMeV = 1000 * (mcp->E() - mcp->Mass());
4804  if(TMeV < 10) continue;
4805  myprt<<std::setw(8)<<imcp;
4806  myprt<<std::setw(5)<<pdg;
4807  myprt<<std::setw(6)<<(int)TMeV;
4808  int eveID = pi_serv->ParticleList().EveId(mcp->TrackId());
4809  int eveIndx = -1;
4810  for(unsigned int jmcp = 0; jmcp < mcpList.size(); ++jmcp) {
4811  if(mcpList[jmcp]->TrackId() == eveID) {
4812  eveIndx = jmcp;
4813  break;
4814  }
4815  } // jmcp
4816  myprt<<std::setw(8)<<eveIndx;
4817  myprt<<" "<<mcp->Process();
4818  myprt<<"\n";
4819  } // imcp
4820  } // mcpList not empty
4821  myprt<<" 'prodID' = <sliceID>:<subSliceIndex>:<productID>/<productUID>\n";
4822  if(prtS3) {
4823  myprt<<"************ Showers ************\n";
4824  myprt<<" prodID Vtx parUID ___ChgPos____ ______Dir_____ ____posInPln____ ___projInPln____ 2D shower UIDs\n";
4825  for(size_t isl = 0; isl < slices.size(); ++isl) {
4826  if(debug.Slice >= 0 && int(isl) != debug.Slice) continue;
4827  auto& slc = slices[isl];
4828  if(slc.showers.empty()) continue;
4829  for(auto& ss3 : slc.showers) Print3S(someText, myprt, ss3);
4830  } // slc
4831  } // prtS3
4832  if(prtP) {
4833  bool printHeader = true;
4834  for(size_t isl = 0; isl < slices.size(); ++isl) {
4835  if(debug.Slice >= 0 && int(isl) != debug.Slice) continue;
4836  auto& slc = slices[isl];
4837  if(slc.pfps.empty()) continue;
4838  for(auto& pfp : slc.pfps) PrintP(someText, myprt, pfp, printHeader);
4839  } // slc
4840  } // prtS3
4841  if(prt3V) {
4842  // print out 3D vertices
4843  myprt<<"****** 3D vertices ******************************************__2DVtx_UID__*******\n";
4844  myprt<<" prodID Cstat TPC X Y Z XEr YEr ZEr pln0 pln1 pln2 Wire score Prim? Nu? nTru";
4845  myprt<<" ___________2D_Pos____________ _____Tj UIDs________\n";
4846  for(size_t isl = 0; isl < slices.size(); ++isl) {
4847  if(debug.Slice >= 0 && int(isl) != debug.Slice) continue;
4848  auto& slc = slices[isl];
4849  if(slc.vtx3s.empty()) continue;
4850  for(auto& vx3 : slc.vtx3s) Print3V(someText, myprt, vx3);
4851  } // slc
4852  } // prt3V
4853  if(prt2V) {
4854  // print out 2D vertices
4855  myprt<<"************ 2D vertices ************\n";
4856  myprt<<" prodID CTP wire err tick err ChiDOF NTj Pass Topo ChgFrac Score v3D Tj UIDs\n";
4857  for(size_t isl = 0; isl < slices.size(); ++isl) {
4858  if(debug.Slice >= 0 && int(isl) != debug.Slice) continue;
4859  auto& slc = slices[isl];
4860  if(slc.vtxs.empty()) continue;
4861  for(auto& vx2 : slc.vtxs) Print2V(someText, myprt, vx2);
4862  } // slc
4863  } // prt2V
4864  if(prtT) {
4865  bool printHeader = true;
4866  for(size_t isl = 0; isl < slices.size(); ++isl) {
4867  if(debug.Slice >= 0 && int(isl) != debug.Slice) continue;
4868  auto& slc = slices[isl];
4869  if(slc.tjs.empty()) continue;
4870  for(auto& tj : slc.tjs) PrintT(someText, myprt, tj, printHeader);
4871  } // slc
4872  } // prtT
4873  } // PrintAll
4874 
4876  void PrintP(std::string someText, mf::LogVerbatim& myprt, PFPStruct& pfp, bool& printHeader)
4877  {
4878  if(pfp.ID <= 0) return;
4879  if(printHeader) {
4880  myprt<<"************ PFParticles ************\n";
4881  myprt<<" prodID sVx _____sPos____ CS _______sDir______ ____sdEdx_____ eVx _____ePos____ CS _______eDir______ ____edEdx_____ MCS Len nTp3 SLk? PDG mcpIndx Par E*P\n";
4882  printHeader = false;
4883  } // printHeader
4884  auto sIndx = GetSliceIndex("P", pfp.UID);
4885  if(sIndx.first == USHRT_MAX) return;
4886  auto& slc = slices[sIndx.first];
4887 // myprt<<someText;
4888  std::string str = std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(pfp.ID);
4889  str += "/" + std::to_string(pfp.UID);
4890  myprt<<std::setw(12)<<str;
4891  // start and end stuff
4892  for(unsigned short startend = 0; startend < 2; ++startend) {
4893  str = "--";
4894  if(pfp.Vx3ID[startend] > 0) str = "3V" + std::to_string(slc.vtx3s[pfp.Vx3ID[startend]-1].UID);
4895  myprt<<std::setw(6)<<str;
4896  myprt<<std::fixed<<std::right<<std::setprecision(0);
4897  myprt<<std::setw(5)<<pfp.XYZ[startend][0];
4898  myprt<<std::setw(5)<<pfp.XYZ[startend][1];
4899  myprt<<std::setw(5)<<pfp.XYZ[startend][2];
4900  // print character for Outside or Inside the FV
4901  if(InsideFV(slc, pfp, startend)) {
4902  myprt<<" I";
4903  } else {
4904  myprt<<" O";
4905  }
4906  myprt<<std::fixed<<std::right<<std::setprecision(2);
4907  myprt<<std::setw(6)<<pfp.Dir[startend][0];
4908  myprt<<std::setw(6)<<pfp.Dir[startend][1];
4909  myprt<<std::setw(6)<<pfp.Dir[startend][2];
4910  for(auto& dedx : pfp.dEdx[startend]) {
4911  if(dedx < 50) {
4912  myprt<<std::setw(5)<<std::setprecision(1)<<dedx;
4913  } else {
4914  myprt<<std::setw(5)<<std::setprecision(0)<<dedx;
4915  }
4916  } // dedx
4917  if (pfp.dEdx[startend].size()<3){
4918  for(size_t i = 0; i<3-pfp.dEdx[startend].size(); ++i){
4919  myprt<<std::setw(6)<<' ';
4920  }
4921  }
4922  } // startend
4923  // global stuff
4924  myprt<<std::setw(7)<<MCSMom(slc, pfp.TjIDs);
4925  float length = PosSep(pfp.XYZ[0], pfp.XYZ[1]);
4926  if(length < 100) {
4927  myprt<<std::setw(5)<<std::setprecision(1)<<length;
4928  } else {
4929  myprt<<std::setw(5)<<std::setprecision(0)<<length;
4930  }
4931  myprt<<std::setw(5)<<std::setprecision(2)<<pfp.Tp3s.size();
4932  myprt<<std::setw(3)<<IsShowerLike(slc, pfp.TjIDs);
4933  myprt<<std::setw(5)<<pfp.PDGCode;
4934  if(pfp.mcpListIndex == UINT_MAX) {
4935  myprt<<" --";
4936  } else {
4937  myprt<<std::setw(8)<<pfp.mcpListIndex;
4938  }
4939  myprt<<std::setw(4)<<pfp.ParentUID;
4940 // myprt<<std::setw(5)<<PrimaryUID(slc, pfp);
4941  myprt<<std::setw(5)<<std::setprecision(2)<<pfp.EffPur;
4942  if(!pfp.TjIDs.empty()) {
4943  if(pfp.TjUIDs.empty()) {
4944  // print Tjs in one TPC
4945  for(auto tjid : pfp.TjIDs) myprt<<" T"<<slc.tjs[tjid - 1].UID;
4946  } else {
4947  // print Tjs in all TPCs (if this is called after FinishEvent)
4948  for(auto tjuid : pfp.TjUIDs) myprt<<" T"<<tjuid;
4949  }
4950  } // TjIDs exist
4951  if(!pfp.DtrUIDs.empty()) {
4952  myprt<<" dtrs";
4953  for(auto dtruid : pfp.DtrUIDs) myprt<<" P"<<dtruid;
4954  } // dtr ids exist
4955  myprt<<"\n";
4956  } // PrintP
4957 
4959  void Print3V(std::string someText, mf::LogVerbatim& myprt, Vtx3Store& vx3)
4960  {
4961  // print a 3D vertex on one line
4962  if(vx3.ID <= 0) return;
4963  auto sIndx = GetSliceIndex("3V", vx3.UID);
4964  if(sIndx.first == USHRT_MAX) return;
4965  auto& slc = slices[sIndx.first];
4966 // myprt<<someText;
4967  std::string str = std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(vx3.ID);
4968  str += "/" + std::to_string(vx3.UID);
4969  myprt<<std::right<<std::setw(12)<<std::fixed<<str;
4970  myprt<<std::setprecision(1);
4971  myprt<<std::right<<std::setw(7)<<vx3.TPCID.Cryostat;
4972  myprt<<std::right<<std::setw(5)<<vx3.TPCID.TPC;
4973  myprt<<std::right<<std::setw(8)<<vx3.X;
4974  myprt<<std::right<<std::setw(8)<<vx3.Y;
4975  myprt<<std::right<<std::setw(8)<<vx3.Z;
4976  myprt<<std::right<<std::setw(5)<<vx3.XErr;
4977  myprt<<std::right<<std::setw(5)<<vx3.YErr;
4978  myprt<<std::right<<std::setw(5)<<vx3.ZErr;
4979  for(auto vx2id : vx3.Vx2ID) {
4980  if(vx2id > 0) {
4981  str = "2V" + std::to_string(slc.vtxs[vx2id - 1].UID);
4982  myprt<<std::right<<std::setw(5)<<str;
4983  } else {
4984  myprt<<" --";
4985  }
4986  } // vx2id
4987  myprt<<std::right<<std::setw(5)<<vx3.Wire;
4988  unsigned short nTruMatch = 0;
4989  for(unsigned short ipl = 0; ipl < slc.nPlanes; ++ipl) {
4990  if(vx3.Vx2ID[ipl] == 0) continue;
4991  unsigned short iv2 = vx3.Vx2ID[ipl] - 1;
4992  if(slc.vtxs[iv2].Stat[kVtxTruMatch]) ++nTruMatch;
4993  } // ipl
4994  myprt<<std::right<<std::setw(6)<<std::setprecision(1)<<vx3.Score;
4995  myprt<<std::setw(6)<<vx3.Primary;
4996  myprt<<std::setw(4)<<vx3.Neutrino;
4997  myprt<<std::right<<std::setw(5)<<nTruMatch;
4998  Point2_t pos;
4999  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5000  PosInPlane(slc, vx3, plane, pos);
5001  myprt<<" "<<PrintPos(slc, pos);
5002  } // plane
5003  if(vx3.Wire == -2) {
5004  // find the Tjs that are attached to it
5005  for(unsigned short end = 0; end < 2; ++end) {
5006  for(auto& pfp : slc.pfps) {
5007  if(pfp.Vx3ID[end] == vx3.ID) {
5008  for(auto tjID : pfp.TjIDs) {
5009  auto& tj = slc.tjs[tjID - 1];
5010  myprt<<" T"<<tj.UID;
5011  } // tjID
5012  } // pfp.Vx3ID[0] == vx3.ID
5013  } // pfp
5014  } // end
5015  } else {
5016  auto vxtjs = GetAssns(slc, "3V", vx3.ID, "T");
5017  for(auto tjid : vxtjs) {
5018  auto& tj = slc.tjs[tjid - 1];
5019  myprt<<" T"<<tj.UID;
5020  }
5021  } // vx3.Wire != -2
5022  myprt<<"\n";
5023  } // Print3V
5024 
5026  void Print2V(std::string someText, mf::LogVerbatim& myprt, VtxStore& vx2)
5027  {
5028  // print a 2D vertex on one line
5029  if(vx2.ID <= 0) return;
5030  if(debug.Plane >= 0 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) return;
5031  auto sIndx = GetSliceIndex("2V", vx2.UID);
5032  if(sIndx.first == USHRT_MAX) return;
5033  auto& slc = slices[sIndx.first];
5034 // myprt<<someText;
5035  std::string str = std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(vx2.ID);
5036  str += "/" + std::to_string(vx2.UID);
5037  myprt<<std::right<<std::setw(12)<<std::fixed<<str;
5038  myprt<<std::right<<std::setw(6)<<vx2.CTP;
5039  myprt<<std::right<<std::setw(8)<<std::setprecision(0)<<std::nearbyint(vx2.Pos[0]);
5040  myprt<<std::right<<std::setw(5)<<std::setprecision(1)<<vx2.PosErr[0];
5041  myprt<<std::right<<std::setw(8)<<std::setprecision(0)<<std::nearbyint(vx2.Pos[1]/tcc.unitsPerTick);
5042  myprt<<std::right<<std::setw(5)<<std::setprecision(1)<<vx2.PosErr[1]/tcc.unitsPerTick;
5043  myprt<<std::right<<std::setw(7)<<vx2.ChiDOF;
5044  myprt<<std::right<<std::setw(5)<<vx2.NTraj;
5045  myprt<<std::right<<std::setw(5)<<vx2.Pass;
5046  myprt<<std::right<<std::setw(6)<<vx2.Topo;
5047  myprt<<std::right<<std::setw(9)<<std::setprecision(2)<<vx2.TjChgFrac;
5048  myprt<<std::right<<std::setw(6)<<std::setprecision(1)<<vx2.Score;
5049  int v3id = 0;
5050  if(vx2.Vx3ID > 0) v3id = slc.vtx3s[vx2.Vx3ID - 1].UID;
5051  myprt<<std::right<<std::setw(5)<<v3id;
5052  myprt<<" ";
5053  // display the traj IDs
5054  for(unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
5055  auto const& tj = slc.tjs[ii];
5056  if(tj.AlgMod[kKilled]) continue;
5057  for(unsigned short end = 0; end < 2; ++end) {
5058  if(tj.VtxID[end] != (short)vx2.ID) continue;
5059  std::string tid = " T" + std::to_string(tj.UID) + "_" + std::to_string(end);
5060  myprt<<std::right<<std::setw(6)<<tid;
5061  } // end
5062  } // ii
5063  // Special flags. Ignore the first flag bit (0 = kVtxTrjTried) which is done for every vertex
5064  for(unsigned short ib = 1; ib < VtxBitNames.size(); ++ib) if(vx2.Stat[ib]) myprt<<" "<<VtxBitNames[ib];
5065  myprt<<"\n";
5066  } // Print2V
5067 
5069  void Print3S(std::string someText, mf::LogVerbatim& myprt, ShowerStruct3D& ss3)
5070  {
5071  if(ss3.ID <= 0) return;
5072  auto sIndx = GetSliceIndex("3S", ss3.UID);
5073  if(sIndx.first == USHRT_MAX) return;
5074  auto& slc = slices[sIndx.first];
5075 // myprt<<someText;
5076  std::string str = std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(ss3.ID);
5077  str += "/" + std::to_string(ss3.UID);
5078  myprt<<std::fixed<<std::setw(12)<<str;
5079  str = "--";
5080  if(ss3.Vx3ID > 0) str = "3V" + std::to_string(slc.vtx3s[ss3.Vx3ID-1].UID);
5081  myprt<<std::setw(6)<<str;
5082  for(unsigned short xyz = 0; xyz < 3; ++xyz) myprt<<std::setprecision(0)<<std::setw(5)<<ss3.ChgPos[xyz];
5083  for(unsigned short xyz = 0; xyz < 3; ++xyz) myprt<<std::setprecision(2)<<std::setw(5)<<ss3.Dir[xyz];
5084  std::vector<float> projInPlane(slc.nPlanes);
5085  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5086  CTP_t inCTP = EncodeCTP(ss3.TPCID.Cryostat, ss3.TPCID.TPC, plane);
5087  auto tp = MakeBareTP(slc, ss3.ChgPos, ss3.Dir, inCTP);
5088  myprt<<" "<<PrintPos(slc, tp.Pos);
5089  projInPlane[plane] = tp.Delta;
5090  } // plane
5091  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5092  myprt<<std::setprecision(2)<<std::setw(5)<<projInPlane[plane];
5093  } // plane
5094  for(auto cid : ss3.CotIDs) {
5095  auto& ss = slc.cots[cid - 1];
5096  str = "2S" + std::to_string(ss.UID);
5097  myprt<<std::setw(5)<<str;
5098 /*
5099  auto& stj = slc.tjs[ss.ShowerTjID - 1];
5100  myprt<<" ST"<<stj.ID;
5101  myprt<<" "<<PrintPos(slc, stj.Pts[stj.EndPt[0]].Pos)<<" - "<<PrintPos(slc, stj.Pts[stj.EndPt[1]].Pos);
5102 */
5103  } // ci
5104  if(ss3.NeedsUpdate) myprt<<" *** Needs update";
5105  myprt<<"\n";
5106  } // Print3S
5107 
5109  void PrintT(std::string someText, mf::LogVerbatim& myprt, Trajectory& tj, bool& printHeader)
5110  {
5111  // print a 2D vertex on one line
5112  if(tj.ID <= 0) return;
5113 // if(tj.AlgMod[kKilled]) return;
5114 
5115  if(printHeader) {
5116  myprt<<"************ Trajectories ************\n";
5117  myprt<<" prodID CTP Pass Pts W:T Ang CS AveQ W:T Ang CS AveQ Chg(k) chgRMS Mom SDr __Vtx__ PDG DirFOM Par Pri NuPar E*P mcpIndex WorkID \n";
5118  printHeader = false;
5119  }
5120  auto sIndx = GetSliceIndex("T", tj.UID);
5121  if(sIndx.first == USHRT_MAX) return;
5122  auto& slc = slices[sIndx.first];
5123 // myprt<<someText;
5124  std::string str = std::to_string(slc.ID) + ":" + std::to_string(sIndx.first) + ":" + std::to_string(tj.ID);
5125  str += "/" + std::to_string(tj.UID);
5126  myprt<<std::fixed<<std::setw(12)<<str;
5127  myprt<<std::setw(6)<<tj.CTP;
5128  myprt<<std::setw(5)<<tj.Pass;
5129  // myprt<<std::setw(5)<<tj.Pts.size();
5130  myprt<<std::setw(5)<<tj.EndPt[1] - tj.EndPt[0] + 1;
5131  unsigned short endPt0 = tj.EndPt[0];
5132  auto& tp0 = tj.Pts[endPt0];
5133  int itick = tp0.Pos[1]/tcc.unitsPerTick;
5134  if(itick < 0) itick = 0;
5135  myprt<<std::setw(6)<<(int)(tp0.Pos[0]+0.5)<<":"<<itick; // W:T
5136  if(itick < 10) { myprt<<" "; }
5137  if(itick < 100) { myprt<<" "; }
5138  if(itick < 1000) { myprt<<" "; }
5139  myprt<<std::setw(6)<<std::setprecision(2)<<tp0.Ang;
5140  myprt<<std::setw(2)<<tp0.AngleCode;
5141  if(tj.StopFlag[0][kBragg]) {
5142  myprt<<"B";
5143  } else if(tj.StopFlag[0][kAtVtx]) {
5144  myprt<<"V";
5145  } else if(tj.StopFlag[0][kAtKink]) {
5146  myprt<<"K";
5147  } else if(tj.StopFlag[0][kAtTj]) {
5148  myprt<<"T";
5149  } else {
5150  myprt<<" ";
5151  }
5152  myprt<<std::setw(5)<<(int)tp0.AveChg;
5153  unsigned short endPt1 = tj.EndPt[1];
5154  auto& tp1 = tj.Pts[endPt1];
5155  itick = tp1.Pos[1]/tcc.unitsPerTick;
5156  myprt<<std::setw(6)<<(int)(tp1.Pos[0]+0.5)<<":"<<itick; // W:T
5157  if(itick < 10) { myprt<<" "; }
5158  if(itick < 100) { myprt<<" "; }
5159  if(itick < 1000) { myprt<<" "; }
5160  myprt<<std::setw(6)<<std::setprecision(2)<<tp1.Ang;
5161  myprt<<std::setw(2)<<tp1.AngleCode;
5162  if(tj.StopFlag[1][kBragg]) {
5163  myprt<<"B";
5164  } else if(tj.StopFlag[1][kAtVtx]) {
5165  myprt<<"V";
5166  } else {
5167  myprt<<" ";
5168  }
5169  myprt<<std::setw(5)<<(int)tp1.AveChg;
5170  myprt<<std::setw(7)<<std::setprecision(1)<<tj.TotChg/1000;
5171  myprt<<std::setw(7)<<std::setprecision(2)<<tj.ChgRMS;
5172  myprt<<std::setw(5)<<tj.MCSMom;
5173  myprt<<std::setw(4)<<tj.StepDir;
5174  int vxid = 0;
5175  if(tj.VtxID[0] > 0) vxid = slc.vtxs[tj.VtxID[0]-1].UID;
5176  myprt<<std::setw(4)<<vxid;
5177  vxid = 0;
5178  if(tj.VtxID[1] > 0) vxid = slc.vtxs[tj.VtxID[1]-1].UID;
5179  myprt<<std::setw(4)<<vxid;
5180  myprt<<std::setw(5)<<tj.PDGCode;
5181  myprt<<std::setw(7)<<std::setprecision(1)<<tj.DirFOM;
5182  myprt<<std::setw(5)<<tj.ParentID;
5183  myprt<<std::setw(5)<<PrimaryID(slc, tj);
5184  myprt<<std::setw(6)<<NeutrinoPrimaryTjID(slc, tj);
5185  myprt<<std::setw(6)<<std::setprecision(2)<<tj.EffPur;
5186  if(tj.mcpListIndex == UINT_MAX) {
5187  myprt<<" --";
5188  } else {
5189  myprt<<std::setw(6)<<tj.mcpListIndex;
5190  }
5191  myprt<<std::setw(7)<<tj.WorkID;
5192  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
5193  myprt<<"\n";
5194  } // PrintT
5195 
5197  void PrintAllTraj(std::string someText, TCSlice& slc, unsigned short itj, unsigned short ipt, bool prtVtx)
5198  {
5199 
5200  mf::LogVerbatim myprt("TC");
5201 
5202  if(prtVtx) {
5203  if(!slc.vtx3s.empty()) {
5204  // print out 3D vertices
5205  myprt<<someText<<"****** 3D vertices ******************************************__2DVtx_ID__*******\n";
5206  myprt<<someText<<" Vtx Cstat TPC X Y Z XEr YEr ZEr pln0 pln1 pln2 Wire score Prim? Nu? nTru";
5207  myprt<<" ___________2D_Pos____________ _____Tjs________\n";
5208  for(unsigned short iv = 0; iv < slc.vtx3s.size(); ++iv) {
5209  if(slc.vtx3s[iv].ID == 0) continue;
5210  const Vtx3Store& vx3 = slc.vtx3s[iv];
5211  myprt<<someText;
5212  std::string vid = "3v" + std::to_string(vx3.ID);
5213  myprt<<std::right<<std::setw(5)<<std::fixed<<vid;
5214  myprt<<std::setprecision(1);
5215  myprt<<std::right<<std::setw(7)<<vx3.TPCID.Cryostat;
5216  myprt<<std::right<<std::setw(5)<<vx3.TPCID.TPC;
5217  myprt<<std::right<<std::setw(8)<<vx3.X;
5218  myprt<<std::right<<std::setw(8)<<vx3.Y;
5219  myprt<<std::right<<std::setw(8)<<vx3.Z;
5220  myprt<<std::right<<std::setw(5)<<vx3.XErr;
5221  myprt<<std::right<<std::setw(5)<<vx3.YErr;
5222  myprt<<std::right<<std::setw(5)<<vx3.ZErr;
5223  myprt<<std::right<<std::setw(5)<<vx3.Vx2ID[0];
5224  myprt<<std::right<<std::setw(5)<<vx3.Vx2ID[1];
5225  myprt<<std::right<<std::setw(5)<<vx3.Vx2ID[2];
5226  myprt<<std::right<<std::setw(5)<<vx3.Wire;
5227  unsigned short nTruMatch = 0;
5228  for(unsigned short ipl = 0; ipl < slc.nPlanes; ++ipl) {
5229  if(vx3.Vx2ID[ipl] == 0) continue;
5230  unsigned short iv2 = vx3.Vx2ID[ipl] - 1;
5231  if(slc.vtxs[iv2].Stat[kVtxTruMatch]) ++nTruMatch;
5232  } // ipl
5233  myprt<<std::right<<std::setw(6)<<std::setprecision(1)<<vx3.Score;
5234  myprt<<std::setw(6)<<vx3.Primary;
5235  myprt<<std::setw(4)<<vx3.Neutrino;
5236  myprt<<std::right<<std::setw(5)<<nTruMatch;
5237  Point2_t pos;
5238  for(unsigned short plane = 0; plane < slc.nPlanes; ++plane) {
5239  PosInPlane(slc, vx3, plane, pos);
5240  myprt<<" "<<PrintPos(slc, pos);
5241  } // plane
5242  if(vx3.Wire == -2) {
5243  // find the Tjs that are attached to it
5244  for(auto& pfp : slc.pfps) {
5245  if(pfp.Vx3ID[0] == slc.vtx3s[iv].ID) {
5246  for(auto& tjID : pfp.TjIDs) myprt<<" t"<<tjID;
5247  }
5248  if(pfp.Vx3ID[1] == slc.vtx3s[iv].ID) {
5249  for(auto& tjID : pfp.TjIDs) myprt<<" t"<<tjID;
5250  }
5251  } // ipfp
5252  } else {
5253  auto vxtjs = GetAssns(slc, "3V", vx3.ID, "T");
5254  for(auto tjid : vxtjs) myprt<<" t"<<tjid;
5255  }
5256  myprt<<"\n";
5257  }
5258  } // slc.vtx3s.size
5259  if(!slc.vtxs.empty()) {
5260  bool foundOne = false;
5261  for(unsigned short iv = 0; iv < slc.vtxs.size(); ++iv) {
5262  auto& vx2 = slc.vtxs[iv];
5263  if(debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
5264  if(vx2.NTraj == 0) continue;
5265  foundOne = true;
5266  } // iv
5267  if(foundOne) {
5268  // print out 2D vertices
5269  myprt<<someText<<"************ 2D vertices ************\n";
5270  myprt<<someText<<" ID CTP wire err tick err ChiDOF NTj Pass Topo ChgFrac Score v3D TjIDs\n";
5271  for(auto& vx2 : slc.vtxs) {
5272  if(vx2.ID == 0) continue;
5273  if(debug.Plane < 3 && debug.Plane != (int)DecodeCTP(vx2.CTP).Plane) continue;
5274  myprt<<someText;
5275  std::string vid = "2v" + std::to_string(vx2.ID);
5276  myprt<<std::right<<std::setw(5)<<std::fixed<<vid;
5277  myprt<<std::right<<std::setw(6)<<vx2.CTP;
5278  myprt<<std::right<<std::setw(8)<<std::setprecision(0)<<std::nearbyint(vx2.Pos[0]);
5279  myprt<<std::right<<std::setw(5)<<std::setprecision(1)<<vx2.PosErr[0];
5280  myprt<<std::right<<std::setw(8)<<std::setprecision(0)<<std::nearbyint(vx2.Pos[1]/tcc.unitsPerTick);
5281  myprt<<std::right<<std::setw(5)<<std::setprecision(1)<<vx2.PosErr[1]/tcc.unitsPerTick;
5282  myprt<<std::right<<std::setw(7)<<vx2.ChiDOF;
5283  myprt<<std::right<<std::setw(5)<<vx2.NTraj;
5284  myprt<<std::right<<std::setw(5)<<vx2.Pass;
5285  myprt<<std::right<<std::setw(6)<<vx2.Topo;
5286  myprt<<std::right<<std::setw(9)<<std::setprecision(2)<<vx2.TjChgFrac;
5287  myprt<<std::right<<std::setw(6)<<std::setprecision(1)<<vx2.Score;
5288  myprt<<std::right<<std::setw(5)<<vx2.Vx3ID;
5289  myprt<<" ";
5290  // display the traj IDs
5291  for(unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
5292  auto const& aTj = slc.tjs[ii];
5293  if(debug.Plane < 3 && debug.Plane != (int)DecodeCTP(aTj.CTP).Plane) continue;
5294  if(aTj.AlgMod[kKilled]) continue;
5295  for(unsigned short end = 0; end < 2; ++end) {
5296  if(aTj.VtxID[end] != (short)vx2.ID) continue;
5297  std::string tid = " t" + std::to_string(aTj.ID) + "_" + std::to_string(end);
5298  myprt<<std::right<<std::setw(6)<<tid;
5299  } // end
5300  } // ii
5301  // Special flags. Ignore the first flag bit (0 = kVtxTrjTried) which is done for every vertex
5302  for(unsigned short ib = 1; ib < VtxBitNames.size(); ++ib) if(vx2.Stat[ib]) myprt<<" "<<VtxBitNames[ib];
5303  myprt<<"\n";
5304  } // iv
5305  }
5306  } // slc.vtxs.size
5307  }
5308 
5309  if(slc.tjs.empty()) {
5310  mf::LogVerbatim("TC")<<someText<<" No allTraj trajectories to print";
5311  return;
5312  }
5313 
5314  // Print all trajectories in slc.tjs if itj == USHRT_MAX
5315  // Print a single traj (itj) and a single TP (ipt) or all TPs (USHRT_MAX)
5316  if(itj == USHRT_MAX) {
5317  // Print summary trajectory information
5318  std::vector<unsigned int> tmp;
5319 // myprt<<someText<<" TRJ ID CTP Pass Pts W:T Ang CS AveQ dEdx W:T Ang CS AveQ dEdx Chg(k) chgRMS Mom SDr __Vtx__ PDG Par Pri NuPar TRuPDG E*P TruKE WorkID \n";
5320  myprt<<someText<<" UID CTP Pass Pts W:T Ang CS AveQ fnTj W:T Ang CS AveQ fnTj Chg(k) chgRMS Mom SDr __Vtx__ PDG DirFOM Par Pri NuPar TRuPDG E*P TruKE WorkID \n";
5321  for(unsigned short ii = 0; ii < slc.tjs.size(); ++ii) {
5322  auto& aTj = slc.tjs[ii];
5323  if(debug.Plane >=0 && debug.Plane < 3 && debug.Plane != (int)DecodeCTP(aTj.CTP).Plane) continue;
5324  myprt<<someText<<" ";
5325  std::string tid;
5326  if(aTj.AlgMod[kKilled]) { tid = "k" + std::to_string(aTj.UID); } else { tid = "t" + std::to_string(aTj.UID); }
5327  myprt<<std::fixed<<std::setw(5)<<tid;
5328  myprt<<std::setw(6)<<aTj.CTP;
5329  myprt<<std::setw(5)<<aTj.Pass;
5330 // myprt<<std::setw(5)<<aTj.Pts.size();
5331  myprt<<std::setw(5)<<aTj.EndPt[1] - aTj.EndPt[0] + 1;
5332  unsigned short endPt0 = aTj.EndPt[0];
5333  auto& tp0 = aTj.Pts[endPt0];
5334  int itick = tp0.Pos[1]/tcc.unitsPerTick;
5335  if(itick < 0) itick = 0;
5336  myprt<<std::setw(6)<<(int)(tp0.Pos[0]+0.5)<<":"<<itick; // W:T
5337  if(itick < 10) { myprt<<" "; }
5338  if(itick < 100) { myprt<<" "; }
5339  if(itick < 1000) { myprt<<" "; }
5340  myprt<<std::setw(6)<<std::setprecision(2)<<tp0.Ang;
5341  myprt<<std::setw(2)<<tp0.AngleCode;
5342  if(aTj.StopFlag[0][kBragg]) {
5343  myprt<<"B";
5344  } else if(aTj.StopFlag[0][kAtVtx]) {
5345  myprt<<"V";
5346  } else if(aTj.StopFlag[0][kAtKink]) {
5347  myprt<<"K";
5348  } else if(aTj.StopFlag[0][kAtTj]) {
5349  myprt<<"T";
5350  } else {
5351  myprt<<" ";
5352  }
5353  myprt<<std::setw(5)<<(int)tp0.AveChg;
5354  // Print the fraction of points in the first half that are in a shower
5355  float frac = 0;
5356  float cnt = 0;
5357  unsigned short midPt = 0.5 * (aTj.EndPt[0] + aTj.EndPt[1]);
5358  for(unsigned short ipt = aTj.EndPt[0]; ipt < midPt; ++ipt) {
5359  auto& tp = aTj.Pts[ipt];
5360 // if(tp.Environment[kEnvNearShower]) ++frac;
5361  if(tp.Environment[kEnvNearTj]) ++frac;
5362  ++cnt;
5363  } // ipt
5364  if(cnt > 0) frac /= cnt;
5365  myprt<<std::setw(5)<<std::setprecision(1)<<frac;
5366 /* print NearInShower fraction instead
5367  unsigned short prec = 1;
5368  if(aTj.dEdx[0] > 99) prec = 0;
5369  myprt<<std::setw(5)<<std::setprecision(prec)<<aTj.dEdx[0];
5370 */
5371  unsigned short endPt1 = aTj.EndPt[1];
5372  auto& tp1 = aTj.Pts[endPt1];
5373  itick = tp1.Pos[1]/tcc.unitsPerTick;
5374  myprt<<std::setw(6)<<(int)(tp1.Pos[0]+0.5)<<":"<<itick; // W:T
5375  if(itick < 10) { myprt<<" "; }
5376  if(itick < 100) { myprt<<" "; }
5377  if(itick < 1000) { myprt<<" "; }
5378  myprt<<std::setw(6)<<std::setprecision(2)<<tp1.Ang;
5379  myprt<<std::setw(2)<<tp1.AngleCode;
5380  if(aTj.StopFlag[1][kBragg]) {
5381  myprt<<"B";
5382  } else if(aTj.StopFlag[1][kAtVtx]) {
5383  myprt<<"V";
5384  } else {
5385  myprt<<" ";
5386  }
5387  myprt<<std::setw(5)<<(int)tp1.AveChg;
5388  // Print the fraction of points in the second half that are NearInShower
5389  frac = 0;
5390  cnt = 0;
5391  for(unsigned short ipt = midPt; ipt <= aTj.EndPt[1]; ++ipt) {
5392  auto& tp = aTj.Pts[ipt];
5393 // if(tp.Environment[kEnvNearShower]) ++frac;
5394  if(tp.Environment[kEnvNearTj]) ++frac;
5395  ++cnt;
5396  } // ipt
5397  if(cnt > 0) frac /= cnt;
5398  myprt<<std::setw(5)<<std::setprecision(1)<<frac;
5399 /*
5400  prec = 1;
5401  if(aTj.dEdx[1] > 99) prec = 0;
5402  myprt<<std::setw(5)<<std::setprecision(prec)<<aTj.dEdx[1];
5403 */
5404  myprt<<std::setw(7)<<std::setprecision(1)<<aTj.TotChg/1000;
5405  myprt<<std::setw(7)<<std::setprecision(2)<<aTj.ChgRMS;
5406  myprt<<std::setw(5)<<aTj.MCSMom;
5407  myprt<<std::setw(4)<<aTj.StepDir;
5408  myprt<<std::setw(4)<<aTj.VtxID[0];
5409  myprt<<std::setw(4)<<aTj.VtxID[1];
5410  myprt<<std::setw(5)<<aTj.PDGCode;
5411  myprt<<std::setw(7)<<std::setprecision(1)<<aTj.DirFOM;
5412  myprt<<std::setw(5)<<aTj.ParentID;
5413  myprt<<std::setw(5)<<PrimaryID(slc, aTj);
5414  myprt<<std::setw(6)<<NeutrinoPrimaryTjID(slc, aTj);
5415  int truKE = 0;
5416  int pdg = 0;
5417 /*
5418  if(aTj.mcpListIndex < evt.mcpList.size()) {
5419  auto& mcp = evt.mcpList[aTj.mcpListIndex];
5420  truKE = 1000 * (mcp->E() - mcp->Mass());
5421  pdg = mcp->PdgCode();
5422  }
5423 */
5424  myprt<<std::setw(6)<<pdg;
5425  myprt<<std::setw(6)<<std::setprecision(2)<<aTj.EffPur;
5426  myprt<<std::setw(5)<<truKE;
5427  myprt<<std::setw(7)<<aTj.WorkID;
5428  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(aTj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
5429  myprt<<"\n";
5430  } // ii
5431  return;
5432  } // itj > slc.tjs.size()-1
5433 
5434  if(itj > slc.tjs.size()-1) return;
5435 
5436  auto const& aTj = slc.tjs[itj];
5437 
5438  mf::LogVerbatim("TC")<<"Print slc.tjs["<<itj<<"] Vtx[0] "<<aTj.VtxID[0]<<" Vtx[1] "<<aTj.VtxID[1];
5439  myprt<<"AlgBits";
5440  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(aTj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
5441  myprt<<"\n";
5442 
5443  PrintHeader(someText);
5444  if(ipt == USHRT_MAX) {
5445  // print all points
5446  for(unsigned short ii = 0; ii < aTj.Pts.size(); ++ii) PrintTrajPoint(someText, slc, ii, aTj.StepDir, aTj.Pass, aTj.Pts[ii]);
5447  } else {
5448  // print just one
5449  PrintTrajPoint(someText, slc, ipt, aTj.StepDir, aTj.Pass, aTj.Pts[ipt]);
5450  }
5451  } // PrintAllTraj
5452 
5453 
5455  void PrintTrajectory(std::string someText, TCSlice& slc, const Trajectory& tj, unsigned short tPoint)
5456  {
5457  // prints one or all trajectory points on tj
5458 
5459  int trupdg = 0;
5460 // if(tj.mcpListIndex < evt.mcpList.size()) trupdg = evt.mcpList[tj.mcpListIndex]->PdgCode();
5461 
5462  if(tPoint == USHRT_MAX) {
5463  if(tj.ID < 0) {
5464  mf::LogVerbatim myprt("TC");
5465  myprt<<someText<<" ";
5466  myprt<<"Work: UID "<<tj.UID<<" CTP "<<tj.CTP<<" StepDir "<<tj.StepDir<<" PDG "<<tj.PDGCode<<" TruPDG "<<trupdg<<" slc.vtxs "<<tj.VtxID[0]<<" "<<tj.VtxID[1]<<" nPts "<<tj.Pts.size()<<" EndPts "<<tj.EndPt[0]<<" "<<tj.EndPt[1];
5467  myprt<<" MCSMom "<<tj.MCSMom;
5468  myprt<<" StopFlags "<<PrintStopFlag(tj, 0)<<" "<<PrintStopFlag(tj, 1);
5469  myprt<<" AlgMod names:";
5470  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
5471  } else {
5472  mf::LogVerbatim myprt("TC");
5473  myprt<<someText<<" ";
5474  myprt<<"slc.tjs: UID "<<tj.UID<<" WorkID "<<tj.WorkID<<" StepDir "<<tj.StepDir<<" PDG "<<tj.PDGCode<<" TruPDG "<<trupdg<<" slc.vtxs "<<tj.VtxID[0]<<" "<<tj.VtxID[1]<<" nPts "<<tj.Pts.size()<<" EndPts "<<tj.EndPt[0]<<" "<<tj.EndPt[1];
5475  myprt<<" MCSMom "<<tj.MCSMom;
5476  myprt<<" StopFlags "<<PrintStopFlag(tj, 0)<<" "<<PrintStopFlag(tj, 1);
5477  myprt<<" AlgMod names:";
5478  for(unsigned short ib = 0; ib < AlgBitNames.size(); ++ib) if(tj.AlgMod[ib]) myprt<<" "<<AlgBitNames[ib];
5479  }
5480  PrintHeader(someText);
5481  for(unsigned short ipt = 0; ipt < tj.Pts.size(); ++ipt) PrintTrajPoint(someText, slc, ipt, tj.StepDir, tj.Pass, tj.Pts[ipt]);
5482  // See if this trajectory is a shower Tj
5483  if(tj.AlgMod[kShowerTj]) {
5484  for(unsigned short ic = 0; ic < slc.cots.size(); ++ic) {
5485  if(slc.cots[ic].TjIDs.empty()) continue;
5486  // only print out the info for the correct Tj
5487  if(slc.cots[ic].ShowerTjID != tj.ID) continue;
5488  const ShowerStruct& ss = slc.cots[ic];
5489  mf::LogVerbatim myprt("TC");
5490  myprt<<"cots index "<<ic<<" ";
5491  myprt<<someText<<" Envelope";
5492  if(ss.Envelope.empty()) {
5493  myprt<<" NA";
5494  } else {
5495  for(auto& vtx : ss.Envelope) myprt<<" "<<(int)vtx[0]<<":"<<(int)(vtx[1]/tcc.unitsPerTick);
5496  }
5497  myprt<<" Energy "<<(int)ss.Energy;
5498  myprt<<" Area "<<std::fixed<<std::setprecision(1)<<(int)ss.EnvelopeArea<<" ChgDensity "<<ss.ChgDensity;
5499  myprt<<"\nInShower TjIDs";
5500  for(auto& tjID : ss.TjIDs) {
5501  myprt<<" "<<tjID;
5502  } // tjIDA_Klystron_4U
5503 
5504  myprt<<"\n";
5505  myprt<<"NearTjIDs";
5506  for(auto& tjID : ss.NearTjIDs) {
5507  myprt<<" "<<tjID;
5508  } // tjID
5509  myprt<<"\n";
5510  myprt<<"\n";
5511  myprt<<"Angle "<<std::fixed<<std::setprecision(2)<<ss.Angle<<" +/- "<<ss.AngleErr;
5512  myprt<<" AspectRatio "<<std::fixed<<std::setprecision(2)<<ss.AspectRatio;
5513  myprt<<" DirectionFOM "<<std::fixed<<std::setprecision(2)<<ss.DirectionFOM;
5514  if(ss.ParentID > 0) {
5515  myprt<<" Parent Tj "<<ss.ParentID<<" FOM "<<ss.ParentFOM;
5516  } else {
5517  myprt<<" No parent";
5518  }
5519  myprt<<" TruParentID "<<ss.TruParentID<<" SS3ID "<<ss.SS3ID<<"\n";
5520  if(ss.NeedsUpdate) myprt<<"*********** This shower needs to be updated ***********";
5521  myprt<<"................................................";
5522  } // ic
5523  } // Shower Tj
5524  } else {
5525  // just print one traj point
5526  if(tPoint > tj.Pts.size() -1) {
5527  mf::LogVerbatim("TC")<<"Can't print non-existent traj point "<<tPoint;
5528  return;
5529  }
5530  PrintTrajPoint(someText, slc, tPoint, tj.StepDir, tj.Pass, tj.Pts[tPoint]);
5531  }
5532  } // PrintTrajectory
5533 
5535  void PrintHeader(std::string someText)
5536  {
5537  mf::LogVerbatim("TC")<<someText<<" TRP CTP Ind Stp W:Tick Delta RMS Ang C Err Dir0 Dir1 Q AveQ Pull FitChi NTPF Hits ";
5538  } // PrintHeader
5539 
5541  void PrintTrajPoint(std::string someText, TCSlice& slc, unsigned short ipt, short dir, unsigned short pass, TrajPoint const& tp)
5542  {
5543  mf::LogVerbatim myprt("TC");
5544  myprt<<someText<<" TRP"<<std::fixed;
5545  myprt<<pass;
5546  if(dir > 0) { myprt<<"+"; } else { myprt<<"-"; }
5547  myprt<<std::setw(6)<<tp.CTP;
5548  myprt<<std::setw(5)<<ipt;
5549  myprt<<std::setw(5)<<tp.Step;
5550  myprt<<std::setw(7)<<std::setprecision(1)<<tp.Pos[0]<<":"<<tp.Pos[1]/tcc.unitsPerTick; // W:T
5551  if(tp.Pos[1] < 10) { myprt<<" "; }
5552  if(tp.Pos[1] < 100) { myprt<<" "; }
5553  if(tp.Pos[1] < 1000) { myprt<<" "; }
5554  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Delta;
5555  myprt<<std::setw(6)<<std::setprecision(2)<<tp.DeltaRMS;
5556  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Ang;
5557  myprt<<std::setw(2)<<tp.AngleCode;
5558  myprt<<std::setw(6)<<std::setprecision(2)<<tp.AngErr;
5559  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Dir[0];
5560  myprt<<std::setw(6)<<std::setprecision(2)<<tp.Dir[1];
5561  myprt<<std::setw(7)<<(int)tp.Chg;
5562  myprt<<std::setw(8)<<(int)tp.AveChg;
5563  myprt<<std::setw(6)<<std::setprecision(1)<<tp.ChgPull;
5564  myprt<<std::setw(7)<<tp.FitChi;
5565  myprt<<std::setw(6)<<tp.NTPsFit;
5566  // print the hits associated with this traj point
5567  if(tp.Hits.size() > 16) {
5568  // don't print too many hits (e.g. from a shower Tj)
5569  myprt<<" "<<tp.Hits.size()<<" shower hits";
5570  } else {
5571  for(unsigned short ii = 0; ii < tp.Hits.size(); ++ii) {
5572  unsigned int iht = tp.Hits[ii];
5573  auto& hit = (*evt.allHits)[slc.slHits[iht].allHitsIndex];
5574  myprt<<" "<<hit.WireID().Wire<<":"<<(int)hit.PeakTime();
5575  if(tp.UseHit[ii]) {
5576  // Distinguish used hits from nearby hits
5577  myprt<<"_";
5578  } else {
5579  myprt<<"x";
5580  }
5581  myprt<<slc.slHits[iht].InTraj;
5582  } // iht
5583  }
5584  } // PrintTrajPoint
5585 
5587  void PrintPFP(std::string someText, TCSlice& slc, const PFPStruct& pfp, bool printHeader)
5588  {
5589  mf::LogVerbatim myprt("TC");
5590  if(printHeader) {
5591  myprt<<someText;
5592  myprt<<" PFP sVx ________sPos_______ CS _______sDir______ ____sdEdx_____ eVx ________ePos_______ CS _______eDir______ ____edEdx____ Len nTp3 MCSMom ShLike? PDG mcpIndx Par Prim E*P\n";
5593  }
5594  myprt<<someText;
5595  std::string pid = "P" + std::to_string(pfp.ID);
5596  myprt<<std::setw(5)<<pid;
5597  // start and end stuff
5598  for(unsigned short startend = 0; startend < 2; ++startend) {
5599  myprt<<std::setw(4)<<pfp.Vx3ID[startend];
5600  myprt<<std::fixed<<std::right<<std::setprecision(1);
5601  myprt<<std::setw(7)<<pfp.XYZ[startend][0];
5602  myprt<<std::setw(7)<<pfp.XYZ[startend][1];
5603  myprt<<std::setw(7)<<pfp.XYZ[startend][2];
5604  // print character for Outside or Inside the FV
5605  geo::TPCID tpcid;
5606  if(InsideTPC(pfp.XYZ[startend], tpcid)) {
5607  myprt<<" I";
5608  } else {
5609  myprt<<" O";
5610  }
5611  myprt<<std::fixed<<std::right<<std::setprecision(2);
5612  myprt<<std::setw(6)<<pfp.Dir[startend][0];
5613  myprt<<std::setw(6)<<pfp.Dir[startend][1];
5614  myprt<<std::setw(6)<<pfp.Dir[startend][2];
5615  for(auto& dedx : pfp.dEdx[startend]) {
5616  if(dedx < 50) {
5617  myprt<<std::setw(5)<<std::setprecision(1)<<dedx;
5618  } else {
5619  myprt<<std::setw(5)<<std::setprecision(0)<<dedx;
5620  }
5621  } // dedx
5622  if (pfp.dEdx[startend].size()<3){
5623  for(size_t i = 0; i<3-pfp.dEdx[startend].size(); ++i){
5624  myprt<<std::setw(6)<<' ';
5625  }
5626  }
5627  } // startend
5628  // global stuff
5629 // myprt<<std::setw(5)<<pfp.BestPlane;
5630  float length = PosSep(pfp.XYZ[0], pfp.XYZ[1]);
5631  if(length < 100) {
5632  myprt<<std::setw(5)<<std::setprecision(1)<<length;
5633  } else {
5634  myprt<<std::setw(5)<<std::setprecision(0)<<length;
5635  }
5636  myprt<<std::setw(5)<<std::setprecision(2)<<pfp.Tp3s.size();
5637  myprt<<std::setw(7)<<MCSMom(slc, pfp.TjIDs);
5638  myprt<<std::setw(5)<<IsShowerLike(slc, pfp.TjIDs);
5639  myprt<<std::setw(5)<<pfp.PDGCode;
5640 /*
5641  if(pfp.mcpListIndex < evt.mcpList.size()) {
5642  myprt<<std::setw(8)<<pfp.mcpListIndex;
5643  } else {
5644  myprt<<" NA";
5645  }
5646 */
5647  myprt<<" NA";
5648  myprt<<std::setw(4)<<pfp.ParentUID;
5649  myprt<<std::setw(5)<<PrimaryUID(slc, pfp);
5650  myprt<<std::setw(5)<<std::setprecision(2)<<pfp.EffPur;
5651  if(!pfp.TjIDs.empty()) {
5652  for(auto& tjID : pfp.TjIDs) myprt<<" T"<<tjID;
5653  }
5654  if(!pfp.DtrUIDs.empty()) {
5655  myprt<<" dtrs";
5656  for(auto& dtrUID : pfp.DtrUIDs) myprt<<" P"<<dtrUID;
5657  }
5658  } // PrintPFP
5659 
5661  void PrintPFPs(std::string someText, TCSlice& slc)
5662  {
5663  if(slc.pfps.empty()) return;
5664 
5665  mf::LogVerbatim myprt("TC");
5666  myprt<<someText;
5667  myprt<<" PFP sVx ________sPos_______ ______sDir______ ______sdEdx_____ eVx ________ePos_______ ______eDir______ ______edEdx_____ BstPln PDG TruPDG Par Prim E*P\n";
5668  bool printHeader = true;
5669  for(auto& pfp : slc.pfps) {
5670  PrintPFP(someText, slc, pfp, printHeader);
5671  printHeader = false;
5672  } // im
5673 
5674  } // PrintPFPs
5675 
5677  std::string PrintStopFlag(const Trajectory& tj, unsigned short end)
5678  {
5679  if(end > 1) return "Invalid end";
5680  std::string tmp;
5681  bool first = true;
5682  for(unsigned short ib = 0; ib < StopFlagNames.size(); ++ib) {
5683  if(tj.StopFlag[end][ib]) {
5684  if(first) {
5685  tmp = std::to_string(end) + ":" + StopFlagNames[ib];
5686  first = false;
5687  } else {
5688  tmp += "," + StopFlagNames[ib];
5689  }
5690  }
5691  } // ib
5692  return tmp;
5693  } // PrintStopFlag
5694 
5696  std::string PrintHitShort(const TCHit& tch)
5697  {
5698  if(tch.allHitsIndex > (*evt.allHits).size() - 1) return "NA";
5699  auto& hit = (*evt.allHits)[tch.allHitsIndex];
5700  return std::to_string(hit.WireID().Plane) + ":" + std::to_string(hit.WireID().Wire) + ":" + std::to_string((int)hit.PeakTime());
5701  } // PrintHit
5702 
5704  std::string PrintHit(const TCHit& tch)
5705  {
5706  if(tch.allHitsIndex > (*evt.allHits).size() - 1) return "NA";
5707  auto& hit = (*evt.allHits)[tch.allHitsIndex];
5708  return std::to_string(hit.WireID().Plane) + ":" + std::to_string(hit.WireID().Wire) + ":" + std::to_string((int)hit.PeakTime()) + "_" + std::to_string(tch.InTraj);
5709  } // PrintHit
5710 
5712  std::string PrintPos(TCSlice& slc, const TrajPoint& tp)
5713  {
5714  return std::to_string(tp.CTP) + ":" + PrintPos(slc, tp.Pos);
5715  } // PrintPos
5716 
5718  std::string PrintPos(TCSlice& slc, const Point2_t& pos)
5719  {
5720  unsigned int wire = 0;
5721  if(pos[0] > -0.4) wire = std::nearbyint(pos[0]);
5722  int time = std::nearbyint(pos[1]/tcc.unitsPerTick);
5723  return std::to_string(wire) + ":" + std::to_string(time);
5724  } // PrintPos
5725 
5726 
5727 } // namespace tca
5728 
Expect tracks entering from the front face. Don&#39;t create neutrino PFParticles.
Definition: DataStructs.h:448
Float_t x
Definition: compare.C:6
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
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
float HitsRMSTime(TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, HitStatus_t hitRequest)
Definition: Utils.cxx:3700
Vector2_t Dir
Definition: DataStructs.h:122
void CheckTrajBeginChg(TCSlice &slc, unsigned short itj)
Definition: Utils.cxx:1406
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
Float_t s
Definition: plot.C:23
Point2_t PosErr
Definition: DataStructs.h:56
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< Trajectory > tjs
vector of all trajectories in each plane
Definition: DataStructs.h:570
std::vector< Vtx3Store > vtx3s
3D vertices
Definition: DataStructs.h:577
Point2_t dEdx
dE/dx for 3D matched trajectories
Definition: DataStructs.h:154
bool dbgStitch
debug PFParticle stitching
Definition: DataStructs.h:520
int PDGCodeVote(TCSlice &slc, std::vector< int > &tjIDs, bool prt)
Definition: Utils.cxx:372
void SetEndPoints(Trajectory &tj)
Definition: Utils.cxx:2864
std::array< std::vector< float >, 2 > dEdxErr
Definition: DataStructs.h:216
Functions to help with numbers.
bool MakeVertexObsolete(std::string fcnLabel, TCSlice &slc, VtxStore &vx2, bool forceKill)
Definition: TCVertex.cxx:3120
std::vector< float > kinkCuts
kink angle, nPts fit, (alternate) kink angle significance
Definition: DataStructs.h:475
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
Double_t xx
Definition: macro.C:12
bool InTrajOK(TCSlice &slc, std::string someText)
Definition: Utils.cxx:1333
geo::Length_t DetHalfWidth(geo::TPCID const &tpcid) const
Returns the half width of the active volume of the specified TPC.
float TjDirFOM(TCSlice &slc, const Trajectory &tj, bool prt)
Definition: Utils.cxx:986
void ChgSlope(TCSlice &slc, Trajectory &tj, unsigned short fromPt, unsigned short toPt, float &slope, float &slopeErr, float &chiDOF)
Definition: Utils.cxx:1296
struct of temporary 2D vertices (end points)
Definition: DataStructs.h:54
std::vector< int > GetAssns(TCSlice &slc, std::string type1Name, int id, std::string type2Name)
Definition: Utils.cxx:4279
void TrajPointTrajDOCA(TCSlice &slc, TrajPoint const &tp, Trajectory const &tj, unsigned short &closePt, float &minSep)
Definition: Utils.cxx:2155
const std::vector< std::string > AlgBitNames
Definition: DataStructs.cxx:13
std::vector< ShowerStruct > cots
Definition: DataStructs.h:580
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:119
Float_t E
Definition: plot.C:23
std::vector< float > maxPos0
Definition: DataStructs.h:489
unsigned short NTPsFit
Definition: DataStructs.h:133
bool InsideTPC(const Point3_t &pos, geo::TPCID &inTPCID)
Definition: PFPUtils.cxx:2825
std::vector< int > NearTjIDs
Definition: DataStructs.h:247
int UID
unique global ID
Definition: DataStructs.h:287
std::array< double, 3 > Point3_t
Definition: DataStructs.h:35
std::vector< ShowerStruct3D > showers
Definition: DataStructs.h:582
unsigned int index
Definition: StepUtils.cxx:5
float PosSep(const Point2_t &pos1, const Point2_t &pos2)
Definition: Utils.cxx:2353
std::vector< Point2_t > Envelope
Definition: DataStructs.h:253
TCConfig tcc
Definition: DataStructs.cxx:6
unsigned short Step
Definition: DataStructs.h:134
void PrintPFP(std::string someText, TCSlice &slc, const PFPStruct &pfp, bool printHeader)
Definition: Utils.cxx:5587
Float_t den
Definition: plot.C:37
std::vector< unsigned int > PutTrajHitsInVector(Trajectory const &tj, HitStatus_t hitRequest)
Definition: Utils.cxx:2416
bool TrajTrajDOCA(TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2, unsigned short &ipt1, unsigned short &ipt2, float &minSep, bool considerDeadWires)
Definition: Utils.cxx:2181
vertex position fixed manually - no fitting done
Definition: DataStructs.h:74
int NeutrinoPrimaryTjID(TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:441
Float_t y
Definition: compare.C:6
Int_t B
Definition: plot.C:25
std::array< std::bitset< 8 >, 2 > StopFlag
Definition: DataStructs.h:165
std::vector< int > Vx2ID
Definition: DataStructs.h:94
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
Geometry information for a single TPC.
Definition: TPCGeo.h:37
bool WireHitRangeOK(TCSlice &slc, const CTP_t &inCTP)
Definition: Utils.cxx:4085
void UnsetUsedHits(TCSlice &slc, TrajPoint &tp)
Definition: Utils.cxx:1162
int ParentID
ID of the parent, or the ID of the Tj this one was merged with if it is killed.
Definition: DataStructs.h:147
std::vector< unsigned int > Hits
Definition: DataStructs.h:136
void TjDeltaRMS(TCSlice &slc, Trajectory &tj, unsigned short firstPt, unsigned short lastPt, double &rms, unsigned short &cnt)
Definition: Utils.cxx:3029
std::vector< int > TjIDs
Definition: DataStructs.h:246
void PrintT(std::string someText, mf::LogVerbatim &myprt, Trajectory &tj, bool &printHeader)
Definition: Utils.cxx:5109
bool StoreTraj(TCSlice &slc, Trajectory &tj)
Definition: Utils.cxx:1175
float TotChg
Total including an estimate for dead wires.
Definition: DataStructs.h:149
Float_t ss
Definition: plot.C:23
unsigned short Pass
Definition: DataStructs.h:58
void FindAlongTrans(Point2_t pos1, Vector2_t dir1, Point2_t pos2, Point2_t &alongTrans)
Definition: Utils.cxx:2817
bool dbgDeltaRayTag
Definition: DataStructs.h:516
virtual double SamplingRate() const =0
Returns the period of the TPC readout electronics clock.
void ChkChgAsymmetry(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:1611
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
float ExpectedHitsRMS(TCSlice &slc, const TrajPoint &tp)
Definition: Utils.cxx:1802
void PrintAllTraj(std::string someText, TCSlice &slc, unsigned short itj, unsigned short ipt, bool prtVtx)
Definition: Utils.cxx:5197
bool TrajClosestApproach(Trajectory const &tj, float x, float y, unsigned short &closePt, float &DOCA)
Definition: Utils.cxx:2377
unsigned short MatchVecIndex(TCSlice &slc, int tjID)
Definition: Utils.cxx:1138
float TrajPointSeparation(TrajPoint &tp1, TrajPoint &tp2)
Definition: Utils.cxx:2368
float length
Definition: TCShower.cxx:6
void SetAngleCode(TrajPoint &tp)
Definition: Utils.cxx:722
void PrintP(std::string someText, mf::LogVerbatim &myprt, PFPStruct &pfp, bool &printHeader)
Definition: Utils.cxx:4876
Float_t tmp
Definition: plot.C:37
void Print3V(std::string someText, mf::LogVerbatim &myprt, Vtx3Store &vx3)
Definition: Utils.cxx:4959
void TagDeltaRays(TCSlice &slc, const CTP_t &inCTP)
Definition: Utils.cxx:3065
bool SetMag(Vector2_t &v1, double mag)
Definition: Utils.cxx:2805
bool dbgSlc
debug only in the user-defined slice? default is all slices
Definition: DataStructs.h:507
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
bool SignalBetween(TCSlice &slc, TrajPoint tp, float toPos0, const float &MinWireSignalFraction)
Definition: Utils.cxx:1695
int EveId(const int trackID) const
std::vector< unsigned int > lastWire
the last wire with a hit
Definition: DataStructs.h:555
HitStatus_t
Definition: Utils.h:36
float GoodnessOfFit() const
Degrees of freedom in the determination of the hit signal shape (-1 by default)
Definition: Hit.h:229
bool LongPulseHit(const recob::Hit &hit)
Definition: Utils.cxx:3936
std::array< int, 2 > Vx3ID
Definition: DataStructs.h:217
short int Multiplicity() const
How many hits could this one be shared with.
Definition: Hit.h:227
bool IsShowerLike(TCSlice &slc, const std::vector< int > TjIDs)
Definition: TCShower.cxx:2002
float PointTrajDOCA(TCSlice &slc, float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2280
std::vector< int > CotIDs
Definition: DataStructs.h:283
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
bool StoreVertex(TCSlice &slc, VtxStore &vx)
Definition: TCVertex.cxx:2093
void PrintPFPs(std::string someText, TCSlice &slc)
Definition: Utils.cxx:5661
std::vector< TrajPoint3 > Tp3s
Definition: DataStructs.h:210
void SetPDGCode(TCSlice &slc, Trajectory &tj, bool tjDone)
Definition: Utils.cxx:3802
std::string PrintPos(TCSlice &slc, const Point2_t &pos)
Definition: Utils.cxx:5718
unsigned int Nwires(unsigned int p, unsigned int tpc=0, unsigned int cstat=0) const
Returns the total number of wires in the specified plane.
bool MergeTjIntoPFP(TCSlice &slc, int mtjid, PFPStruct &pfp, bool prt)
Definition: Utils.cxx:507
int PrimaryID(TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:463
double DeltaAngle2(double Ang1, double Ang2)
Definition: Utils.cxx:2848
std::vector< T > SetIntersection(const std::vector< T > &set1, const std::vector< T > &set2)
Definition: Utils.h:231
std::vector< float > angleRanges
list of max angles for each angle range
Definition: DataStructs.h:486
virtual double ConvertXToTicks(double X, int p, int t, int c) const =0
unsigned short Pass
the pass on which it was created
Definition: DataStructs.h:161
bool dbg3V
debug 3D vertex finding
Definition: DataStructs.h:514
float DeadWireCount(TCSlice &slc, const float &inWirePos1, const float &inWirePos2, CTP_t tCTP)
Definition: Utils.cxx:1887
void Reverse3DMatchTjs(TCSlice &slc, PFPStruct &pfp, bool prt)
Definition: Utils.cxx:1078
void UpdateVxEnvironment(std::string inFcnLabel, TCSlice &slc, VtxStore &vx2, bool prt)
Definition: Utils.cxx:3385
float HitsPosTick(TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:3742
TrajPoint MakeBareTP(TCSlice &slc, Point3_t &pos, Vector3_t &dir, CTP_t inCTP)
Definition: Utils.cxx:3523
std::string PrintHitShort(const TCHit &tch)
Definition: Utils.cxx:5696
unsigned int mcpListIndex
Definition: DataStructs.h:164
std::vector< std::vector< std::pair< int, int > > > wireHitRange
Definition: DataStructs.h:575
unsigned short NumDeltaRays(TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:426
tagged as a vertex between Tjs that are matched to MC truth neutrino interaction particles ...
Definition: DataStructs.h:77
unsigned int Hit
set to the hit index in evt.allHits if a Plane:Wire:Tick match is found
Definition: DebugStruct.h:25
std::vector< int > TjUIDs
Definition: DataStructs.h:208
float MaxTjLen(TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:2322
int Cryostat
Select Cryostat.
Definition: DebugStruct.h:19
struct of temporary 3D vertices
Definition: DataStructs.h:84
unsigned int Nplanes(unsigned int tpc=0, unsigned int cstat=0) const
Returns the total number of wire planes in the specified TPC.
short nPtsAve
dump trajectory points
Definition: DataStructs.h:523
geo::Length_t DetHalfHeight(geo::TPCID const &tpcid) const
Returns the half height of the active volume of the specified TPC.
TCanvas * c1
Definition: plotHisto.C:7
bool dbgStp
debug stepping using debug.Cryostat, debug.TPC, etc
Definition: DataStructs.h:508
int Wire
Select hit Wire for debugging.
Definition: DebugStruct.h:23
unsigned int mcpListIndex
Definition: DataStructs.h:225
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 ChgFracBetween(TCSlice &slc, TrajPoint tp, float toPos0)
Definition: Utils.cxx:1702
float unitsPerTick
scale factor from Tick to WSE equivalent units
Definition: DataStructs.h:488
std::array< Point3_t, 2 > XYZ
Definition: DataStructs.h:212
const detinfo::DetectorProperties * detprop
Definition: DataStructs.h:494
unsigned short NearestPtWithChg(TCSlice &slc, Trajectory &tj, unsigned short thePt)
Definition: Utils.cxx:2966
bool TrajIsClean(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:2891
int WorkID
Select the StartWorkID for debugging.
Definition: DebugStruct.h:26
void DefineHitPos(TCSlice &slc, TrajPoint &tp)
Definition: StepUtils.cxx:1840
void Print2V(std::string someText, mf::LogVerbatim &myprt, VtxStore &vx2)
Definition: Utils.cxx:5026
geo::TPCID TPCID
Definition: DataStructs.h:93
DebugStuff debug
Definition: DebugStruct.cxx:4
CTP_t CTP
Cryostat, TPC, Plane code.
Definition: DataStructs.h:144
void DumpTj()
Definition: Utils.cxx:4728
int UID
unique global ID
Definition: DataStructs.h:96
std::array< Vector3_t, 2 > DirErr
Definition: DataStructs.h:214
bool MergeShowerTjsAndStore(TCSlice &slc, unsigned short istj, unsigned short jstj, bool prt)
Definition: TCShower.cxx:3060
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:34
bool dbg2V
debug 2D vertex finding
Definition: DataStructs.h:510
const std::vector< std::string > StopFlagNames
Definition: DataStructs.cxx:82
bool TrajHitsOK(TCSlice &slc, const unsigned int iht, const unsigned int jht)
Definition: Utils.cxx:1778
std::vector< float > aveHitRMS
average RMS of an isolated hit
Definition: DataStructs.h:541
std::vector< TrajPoint > Pts
Trajectory points.
Definition: DataStructs.h:143
virtual double Temperature() const =0
unsigned short GetPFPIndex(TCSlice &slc, int tjID)
Definition: Utils.cxx:1127
int Plane
Select plane.
Definition: DebugStruct.h:21
std::array< Vector3_t, 2 > Dir
Definition: DataStructs.h:213
std::vector< int > FindCloseTjs(TCSlice &slc, const TrajPoint &fromTp, const TrajPoint &toTp, const float &maxDelta)
Definition: Utils.cxx:2604
float EffPur
Efficiency * Purity.
Definition: DataStructs.h:153
float TrajLength(Trajectory &tj)
Definition: Utils.cxx:2337
void MakeHaloTj(TCSlice &slc, Trajectory &muTj, bool prt)
Definition: Utils.cxx:20
float PointTrajDOCA2(TCSlice &slc, float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2286
void FitTraj(TCSlice &slc, Trajectory &tj, unsigned short originPt, unsigned short npts, short fitDir, TrajPoint &tpFit)
Definition: Utils.cxx:766
double MCSThetaRMS(TCSlice &slc, Trajectory &tj, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:2998
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
bool PointInsideEnvelope(const Point2_t &Point, const std::vector< Point2_t > &Envelope)
Definition: Utils.cxx:2781
std::vector< float > match3DCuts
3D matching cuts
Definition: DataStructs.h:476
void TagJunkTj(TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:2442
void UpdateMatchStructs(TCSlice &slc, int oldTj, int newTj)
Definition: PFPUtils.cxx:160
geo::Length_t DetLength(geo::TPCID const &tpcid) const
Returns the length of the active volume of the specified TPC.
void MakeTrajectoryObsolete(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:1922
short StartEnd
The starting end (-1 = don&#39;t know)
Definition: DataStructs.h:163
unsigned short CloseEnd(TCSlice &slc, const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:2251
std::string PrintHit(const TCHit &tch)
Definition: Utils.cxx:5704
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
bool StartTraj(TCSlice &slc, Trajectory &tj, float fromWire, float fromTick, float toWire, float toTick, CTP_t &tCTP, unsigned short pass)
Definition: Utils.cxx:4454
const geo::GeometryCore * geom
Definition: DataStructs.h:493
bool SplitTraj(TCSlice &slc, unsigned short itj, unsigned short pos, unsigned short ivx, bool prt)
Definition: Utils.cxx:2043
float TpSumHitChg(TCSlice &slc, TrajPoint const &tp)
Definition: Utils.cxx:1853
virtual unsigned int NumberTimeSamples() const =0
int UID
a unique ID for all slices
Definition: DataStructs.h:158
The data type to uniquely identify a TPC.
Definition: geo_types.h:195
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:258
float MaxChargeAsymmetry(TCSlice &slc, std::vector< int > &tjIDs)
Definition: Utils.cxx:340
float EffPur
Efficiency * Purity.
Definition: DataStructs.h:224
View_t View(geo::PlaneID const &pid) const
Returns the view (wire orientation) on the channels of specified TPC plane.
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
void DefineTjParents(TCSlice &slc, bool prt)
Definition: Utils.cxx:129
std::vector< unsigned int > firstWire
the first wire with a hit
Definition: DataStructs.h:554
float TPHitsRMSTime(TCSlice &slc, TrajPoint &tp, HitStatus_t hitRequest)
Definition: Utils.cxx:3668
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 PrintAll(std::string someText, const std::vector< simb::MCParticle * > &mcpList)
Definition: Utils.cxx:4774
void UpdateTjChgProperties(std::string inFcnLabel, TCSlice &slc, Trajectory &tj, bool prt)
Definition: Utils.cxx:3196
float PosSep2(const Point2_t &pos1, const Point2_t &pos2)
Definition: Utils.cxx:2359
bool FindCloseHits(TCSlice &slc, TrajPoint &tp, float const &maxDelta, HitStatus_t hitRequest)
Definition: Utils.cxx:2552
bool CompatibleMerge(TCSlice &slc, const Trajectory &tj1, const Trajectory &tj2, bool prt)
Definition: Utils.cxx:605
std::bitset< 16 > modes
number of points to find AveChg
Definition: DataStructs.h:524
std::vector< TCHit > slHits
Definition: DataStructs.h:569
float TwoTPAngle(TrajPoint &tp1, TrajPoint &tp2)
Definition: Utils.cxx:2407
bool MergeAndStore(TCSlice &slc, unsigned int itj1, unsigned int itj2, bool doPrt)
Definition: Utils.cxx:4095
void RestoreObsoleteTrajectory(TCSlice &slc, unsigned int itj)
Definition: Utils.cxx:1933
bool aveHitRMSValid
set true when the average hit RMS is well-known
Definition: DataStructs.h:549
unsigned short NumPtsWithCharge(TCSlice &slc, const Trajectory &tj, bool includeDeadWires, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:1871
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
unsigned short FarEnd(TCSlice &slc, const Trajectory &tj, const Point2_t &pos)
Definition: Utils.cxx:3646
float ChgFracNearPos(TCSlice &slc, const Point2_t &pos, const std::vector< int > &tjIDs)
Definition: Utils.cxx:2698
unsigned short AngleCode
Definition: DataStructs.h:135
int Tick
Select hit PeakTime for debugging (< 0 for vertex finding)
Definition: DebugStruct.h:24
int TPC
Select TPC.
Definition: DebugStruct.h:20
float val
Definition: StepUtils.cxx:6
raw::ChannelID_t PlaneWireToChannel(WireID const &wireid) const
Returns the ID of the TPC channel connected to the specified wire.
const std::vector< std::string > VtxBitNames
Definition: DataStructs.cxx:91
bool InsideFV(TCSlice &slc, PFPStruct &pfp, unsigned short end)
Definition: PFPUtils.cxx:2810
double DotProd(const Vector3_t &v1, const Vector3_t &v2)
Definition: PFPUtils.h:59
unsigned int CTP_t
Definition: DataStructs.h:41
float ElectronLikelihood(TCSlice &slc, Trajectory &tj, float &asym)
Definition: Utils.cxx:2679
geo::TPCID TPCID
Definition: DataStructs.h:562
bool DecodeDebugString(std::string strng)
Definition: Utils.cxx:4611
bool MakeBareTrajPoint(TCSlice &slc, const TrajPoint &tpIn1, const TrajPoint &tpIn2, TrajPoint &tpOut)
Definition: Utils.cxx:3628
std::bitset< 128 > useAlg
Max hit separation for making junk trajectories. < 0 to turn off.
Definition: DataStructs.h:504
std::string to_string(Flag_t< Storage > const flag)
Convert a flag into a stream (shows its index).
Definition: BitMask.h:187
Float_t norm
bool AnalyzeHits()
Definition: Utils.cxx:3877
TDirectory * dir
Definition: macro.C:5
bool HasDuplicateHits(TCSlice &slc, Trajectory const &tj, bool prt)
Definition: Utils.cxx:2469
std::vector< short > deltaRayTag
min length, min MCSMom and min separation (WSE) for a delta ray tag
Definition: DataStructs.h:470
PFPStruct CreatePFP(TCSlice &slc)
Definition: PFPUtils.cxx:1973
virtual double DriftVelocity(double efield=0., double temperature=0.) const =0
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
unsigned short NumUsedHitsInTj(TCSlice &slc, const Trajectory &tj)
Definition: Utils.cxx:3763
unsigned short NTraj
Definition: DataStructs.h:57
geo::PlaneID DecodeCTP(CTP_t CTP)
unsigned short AngleRange(float angle)
Definition: Utils.cxx:739
Vector2_t PointDirection(const Point2_t p1, const Point2_t p2)
Definition: Utils.cxx:3655
void Print3S(std::string someText, mf::LogVerbatim &myprt, ShowerStruct3D &ss3)
Definition: Utils.cxx:5069
std::vector< int > GetVtxTjIDs(const TCSlice &slc, const VtxStore &vx2)
Definition: TCVertex.cxx:3226
std::vector< int > TjIDs
Definition: DataStructs.h:207
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
unsigned short PDGCodeIndex(int PDGCode)
Definition: Utils.cxx:1908
std::bitset< 8 > Environment
Definition: DataStructs.h:138
std::vector< recob::Hit > const * allHits
Definition: DataStructs.h:536
CTP_t EncodeCTP(unsigned int cryo, unsigned int tpc, unsigned int plane)
Definition: DataStructs.h:45
std::pair< unsigned short, unsigned short > GetSliceIndex(std::string typeName, int uID)
Definition: Utils.cxx:4496
float HitSep2(TCSlice &slc, unsigned int iht, unsigned int jht)
Definition: Utils.cxx:2239
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 HitsPosTime(TCSlice &slc, const std::vector< unsigned int > &hitsInMultiplet, float &sum, HitStatus_t hitRequest)
Definition: Utils.cxx:3736
use the stiff electron strategy
Definition: DataStructs.h:418
float PointTrajSep2(float wire, float time, TrajPoint const &tp)
Definition: Utils.cxx:2262
std::array< double, 3 > Vector3_t
Definition: DataStructs.h:36
int ID
ID of the recob::Slice (not the sub-slice)
Definition: DataStructs.h:564
bool FillWireHitRange(TCSlice &slc)
Definition: Utils.cxx:3944
std::array< std::vector< float >, 2 > dEdx
Definition: DataStructs.h:215
int UID
unique global ID
Definition: DataStructs.h:65
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
unsigned short nPlanes
Definition: DataStructs.h:563
std::vector< MatchStruct > matchVec
3D matching vector
Definition: DataStructs.h:578
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
std::vector< PFPStruct > pfps
Definition: DataStructs.h:579
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:49
void MoveTPToWire(TrajPoint &tp, float wire)
Definition: Utils.cxx:2485
void PosInPlane(const TCSlice &slc, const Vtx3Store &vx3, unsigned short plane, Point2_t &pos)
Definition: TCVertex.cxx:3287
virtual double Efield(unsigned int planegap=0) const =0
Returns the nominal electric field in the specified volume.
void MergeGhostTjs(TCSlice &slc, CTP_t inCTP)
Definition: Utils.cxx:1955
TCEvent evt
Definition: DataStructs.cxx:5
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:27
unsigned int allHitsIndex
Definition: DataStructs.h:529
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
void TrajIntersection(TrajPoint const &tp1, TrajPoint const &tp2, float &x, float &y)
Definition: Utils.cxx:2304
bool StorePFP(TCSlice &slc, PFPStruct &pfp)
Definition: PFPUtils.cxx:2749
bool valIncreasing(SortEntry c1, SortEntry c2)
Definition: Utils.cxx:15
size_t ParentUID
Definition: DataStructs.h:222
short MCSMom(TCSlice &slc, Trajectory &tj, unsigned short firstPt, unsigned short lastPt)
Definition: Utils.cxx:2936
short recoSlice
only reconstruct the slice with ID (0 = all)
Definition: DataStructs.h:506
double DeltaAngle(double Ang1, double Ang2)
Definition: Utils.cxx:2858
void PrintHeader(std::string someText)
Definition: Utils.cxx:5535
Float_t w
Definition: plot.C:23
float TjChgFrac
Fraction of charge near the vertex that is from hits on the vertex Tjs.
Definition: DataStructs.h:68
bool dbgSummary
print a summary report
Definition: DataStructs.h:521
bool NeedsUpdate
Set true when the Tj needs to be updated.
Definition: DataStructs.h:167
master switch for turning on debug mode
Definition: DataStructs.h:449
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 LocalToWorld(const double *tpc, double *world) const
Transform point from local TPC frame to world frame.
Definition: TPCGeo.h:490
bool valDecreasing(SortEntry c1, SortEntry c2)
Definition: Utils.cxx:14
CTP_t CTP
set to an invalid CTP
Definition: DebugStruct.h:22
int PrimaryUID(TCSlice &slc, const PFPStruct &pfp)
Definition: Utils.cxx:482
std::vector< int > DtrUIDs
Definition: DataStructs.h:221
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:230
use the stiff muon strategy
Definition: DataStructs.h:419
float DirFOM
Normalized RMS using ALL hits. Assume it is 50% to start.
Definition: DataStructs.h:151