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