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