LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
CCTrackMaker_module.cc
Go to the documentation of this file.
1 //
3 // CCTrackMaker
4 //
5 // Make tracks using ClusterCrawler clusters and vertex info
6 //
7 // baller@fnal.gov, October 2014
8 // May 2015: Major re-write
9 //
11 
12 // C++ includes
13 #include <cmath>
14 #include <algorithm>
15 #include <iostream>
16 #include <iomanip>
17 #include <fstream>
18 #include <vector>
19 #include <string>
20 
21 // Framework includes
25 #include "fhiclcpp/ParameterSet.h"
31 
32 // LArSoft includes
43 #include "larevt/CalibrationDBI/Interface/ChannelStatusService.h"
44 #include "larevt/CalibrationDBI/Interface/ChannelStatusProvider.h"
45 
50 
51 
52 struct CluLen{
53  int index;
54  int length;
55 };
56 
57 bool greaterThan (CluLen c1, CluLen c2) { return (c1.length > c2.length);}
58 bool lessThan (CluLen c1, CluLen c2) { return (c1.length < c2.length);}
59 
60 namespace trkf {
61 
62  class CCTrackMaker : public art::EDProducer {
63 
64  public:
65 
66  explicit CCTrackMaker(fhicl::ParameterSet const& pset);
67  virtual ~CCTrackMaker();
68 
69  void reconfigure(fhicl::ParameterSet const& p);
70  void produce(art::Event& evt);
71  void beginJob();
72  // void endJob();
73 
74  private:
75 
76  std::string fHitModuleLabel;
77  std::string fClusterModuleLabel;
78  std::string fVertexModuleLabel;
79 
80  // services
83 
86 
87  // Track matching parameters
88  unsigned short algIndex;
89  std::vector<short> fMatchAlgs;
90  std::vector<float> fXMatchErr;
91  std::vector<float> fAngleMatchErr;
92  std::vector<float> fChgAsymFactor;
93  std::vector<float> fMatchMinLen;
94  std::vector<bool> fMakeAlgTracks;
95 
96  // Cluster merging parameters
97  float fMaxDAng;
98  float fChainMaxdX;
99  float fChainVtxAng;
103 
104  float fChgWindow;
105  float fWirePitch;
106  // cosmic ray tagging
109 
110  bool fMakePFPs;
111 
112  // vertex fitting
113  unsigned short fNVtxTrkHitsFit;
115 
116  // temp
117  bool fuBCode;
118 
119  // debugging inputs
120  short fDebugAlg;
121  short fDebugPlane;
124  bool prt;
125 
126  unsigned short nplanes;
127  unsigned int cstat;
128  unsigned int tpc;
129 
130  // hit indexing info
131  std::array<unsigned int, 3> firstWire;
132  std::array<unsigned int, 3> lastWire;
133  std::array<unsigned int, 3> firstHit;
134  std::array<unsigned int, 3> lastHit;
135  std::array<std::vector< std::pair<int, int> >, 3> WireHitRange;
136 
137  std::vector<art::Ptr<recob::Hit>> allhits;
138 
139  // Cluster parameters
140  struct clPar{
141  std::array<float, 2> Wire; // Begin/End Wire
142  std::array<float, 2> X; // Begin/End X
143  std::array<short, 2> Time; // Begin/End Time
144  std::array<float, 2> Slope; // Begin/End slope
145  std::array<float, 2> Charge; // Begin/End charge
146  std::array<float, 2> ChgNear; // Charge near the cluster at each end
147  std::array<float, 2> Angle; // Begin/End angle (radians)
148  std::array<short, 2> Dir; // Product of end * slope
149  std::array<short, 2> VtxIndex; // Vertex index
150  std::array<short, 2> mVtxIndex; // "Maybe" Vertex index
151  std::array<short, 2> BrkIndex; // Broken cluster index
152  std::array<float, 2> MergeError; // Broken cluster merge error (See MakeClusterChains)
153  unsigned short EvtIndex; // index of the cluster in clusterlist
154  short InTrack; // cluster -> chain index
155  unsigned short Length; // cluster length (wires)
156  float TotChg; // total charge of cluster (or series of clusters)
157  };
158  // vector of cluster parameters in each plane
159  std::array<std::vector<clPar>, 3> cls;
160 
161  // cluster chain parameters
162  struct ClsChainPar{
163  std::array<float, 2> Wire; // Begin/End Wire
164  std::array<float, 2> X; // Begin/End X
165  std::array<float, 2> Time; // Begin/End Time
166  std::array<float, 2> Slope; // Begin/End slope
167  std::array<float, 2> Angle; // Begin/End angle (radians)
168  std::array<short, 2> VtxIndex; // Vertex index
169  std::array<short, 2> Dir;
170  std::array<float, 2> ChgNear; // Charge near the cluster at each end
171  std::array<short, 2> mBrkIndex; // a "Maybe" cluster index
172  unsigned short Length;
173  float TotChg;
174  std::vector<unsigned short> ClsIndex;
175  std::vector<unsigned short> Order;
176  short InTrack; // cluster -> track ID (-1 if none, 0 if under construction)
177  };
178  // vector of cluster parameters in each plane
179  std::array<std::vector<ClsChainPar>, 3> clsChain;
180 
181  // 3D Vertex info
182  struct vtxPar{
183  short ID;
184  unsigned short EvtIndex;
185  float X;
186  float Y;
187  float Z;
188  std::array<unsigned short, 3> nClusInPln;
189  bool Neutrino;
190  };
191 
192  std::vector<vtxPar> vtx;
193 
194  // cluster indices assigned to one vertex. Filled in VtxMatch
195  std::array<std::vector<unsigned short>, 3> vxCls;
196 
197  struct TrkPar{
198  short ID;
199  unsigned short Proc; // 1 = VtxMatch, 2 = ...
200  std::array< std::vector<art::Ptr<recob::Hit>>, 3> TrkHits;
201  std::vector<TVector3> TrjPos;
202  std::vector<TVector3> TrjDir;
203  std::array<short, 2> VtxIndex;
204  std::vector<unsigned short> ClsEvtIndices;
205  float Length;
206  short ChgOrder;
207  short MomID;
208  std::array<bool, 2> EndInTPC;
209  std::array<bool, 2> GoodEnd; // set true if there are hits in all planes at the end point
210  std::vector<short> DtrID;
211  short PDGCode;
212  };
213  std::vector<TrkPar> trk;
214 
215  // Array of pointers to hits in each plane for one track
216  std::array< std::vector<art::Ptr<recob::Hit>>, 3> trkHits;
217  // and for one seed
218  std::array< std::vector<art::Ptr<recob::Hit>>, 3> seedHits;
219  // relative charge normalization between planes
220  std::array< float, 3> ChgNorm;
221 
222  // Vector of PFParticle -> track IDs. The first element will be
223  // track ID = 0 indicating that this is a neutrino PFParticle and
224  // there is no associated track
225  std::vector<unsigned short> pfpToTrkID;
226 
227  // characterize the match between clusters in 2 or 3 planes
228  struct MatchPars {
229  std::array<short, 3> Cls;
230  std::array<unsigned short, 3> End;
231  std::array<float, 3> Chg;
232  short Vtx;
233  float dWir; // wire difference at the matching end
234  float dAng; // angle difference at the matching end
235  float dX; // X difference
236  float Err; // Wire,Angle,Time match error
237  short oVtx;
238  float odWir; // wire difference at the other end
239  float odAng; // angle difference at the other end
240  float odX; // time difference at the other end
241  float dSP; // space point difference
242  float oErr; // dAngle dX match error
243  };
244  // vector of many match combinations
245  std::vector<MatchPars> matcomb;
246 
247  void PrintClusters() const;
248 
249  void PrintTracks() const;
250 
251  void MakeClusterChains(art::FindManyP<recob::Hit> const& fmCluHits);
252  float dXClTraj(art::FindManyP<recob::Hit> const& fmCluHits, unsigned short ipl, unsigned short icl1, unsigned short end1, unsigned short icl2);
253  void FillChgNear();
254  void FillWireHitRange();
255 
256  // Find clusters that point to vertices but do not have a
257  // cluster-vertex association made by ClusterCrawler
258  void FindMaybeVertices();
259  // match clusters associated with vertices
260  void VtxMatch(art::FindManyP<recob::Hit> const& fmCluHits);
261  // match clusters in all planes
262  void PlnMatch(art::FindManyP<recob::Hit> const& fmCluHits);
263  // match clusters in all planes
264  void AngMatch(art::FindManyP<recob::Hit> const& fmCluHits);
265 
266  // Make the track/vertex and mother/daughter relationships
267  void MakeFamily();
268  void TagCosmics();
269 
270  void FitVertices();
271 
272  // fill the end matching parameters in the MatchPars struct
273  void FillEndMatch(MatchPars& match);
274  // 2D version
275  void FillEndMatch2(MatchPars& match);
276 
277  float ChargeAsym(std::array<float, 3>& mChg);
278 
279  void FindMidPointMatch(art::FindManyP<recob::Hit> const& fmCluHits, MatchPars& match, unsigned short kkpl, unsigned short kkcl, unsigned short kkend, float& kkWir, float& kkX);
280 
281  bool FindMissingCluster(unsigned short kpl, short& kcl, unsigned short& kend, float kWir, float kX, float okWir, float okX);
282 
283  bool DupMatch(MatchPars& match);
284 
285  void SortMatches(art::FindManyP<recob::Hit> const& fmCluHits, unsigned short procCode);
286 
287  // fill the trkHits array using information.
288  void FillTrkHits(art::FindManyP<recob::Hit> const& fmCluHits, unsigned short imat);
289 
290  // Seed hits for the seed - hit association
291 // void FindSeedHits(unsigned short itk, unsigned short& end);
292 
293  // store the track in the trk vector
294  void StoreTrack(art::FindManyP<recob::Hit> const& fmCluHits, unsigned short imat, unsigned short procCode);
295 
296  // returns the charge along the line between (wire1, time1) and (wire2, time2)
297  float ChargeNear(unsigned short ipl, unsigned short wire1, float time1, unsigned short wire2, float time2);
298 
299  // inflate cuts at large angle
300  float AngleFactor(float slope);
301 
302  }; // class CCTrackMaker
303 
304  //-------------------------------------------------
305  CCTrackMaker::CCTrackMaker(fhicl::ParameterSet const& pset)
306  {
307  this->reconfigure(pset);
308  produces< std::vector<recob::PFParticle> >();
309  produces< art::Assns<recob::PFParticle, recob::Track> >();
310  produces< art::Assns<recob::PFParticle, recob::Cluster> >();
311  produces< art::Assns<recob::PFParticle, recob::Seed> >();
312  produces< art::Assns<recob::PFParticle, recob::Vertex> >();
313  produces< std::vector<recob::Vertex> >();
314  produces< std::vector<recob::Track> >();
315  produces< art::Assns<recob::Track, recob::Hit> >();
316  produces<std::vector<recob::Seed> >();
317  //produces< art::Assns<recob::Seed, recob::Hit> >();
318  }
319 
320  //-------------------------------------------------
321  void CCTrackMaker::reconfigure(fhicl::ParameterSet const& pset)
322  {
323  fHitModuleLabel = pset.get< std::string >("HitModuleLabel");
324  fClusterModuleLabel = pset.get< std::string >("ClusterModuleLabel");
325  fVertexModuleLabel = pset.get< std::string >("VertexModuleLabel");
326  // track matching
327  fMatchAlgs = pset.get< std::vector<short> >("MatchAlgs");
328  fXMatchErr = pset.get< std::vector<float> >("XMatchErr");
329  fAngleMatchErr = pset.get< std::vector<float> >("AngleMatchErr");
330  fChgAsymFactor = pset.get< std::vector<float> >("ChgAsymFactor");
331  fMatchMinLen = pset.get< std::vector<float> >("MatchMinLen");
332  fMakeAlgTracks = pset.get< std::vector<bool> >("MakeAlgTracks");
333  // Cluster merging
334  fMaxDAng = pset.get< float >("MaxDAng");
335  fChainMaxdX = pset.get< float >("ChainMaxdX");
336  fChainVtxAng = pset.get< float >("ChainVtxAng");
337  fMergeChgAsym = pset.get< float >("MergeChgAsym");
338  // Cosmic ray tagging
339  fFiducialCut = pset.get< float >("FiducialCut");
340  fDeltaRayCut = pset.get< float >("DeltaRayCut");
341  // make PFParticles
342  fMakePFPs = pset.get< bool >("MakePFPs");
343  // vertex fitting
344  fNVtxTrkHitsFit = pset.get< unsigned short >("NVtxTrkHitsFit");
345  fHitFitErrFac = pset.get< float >("HitFitErrFac");
346  // uB code
347  fuBCode = pset.get< bool >("uBCode");
348  // debugging inputs
349  fDebugAlg = pset.get< short >("DebugAlg");
350  fDebugPlane = pset.get< short >("DebugPlane");
351  fDebugCluster = pset.get< short >("DebugCluster");
352  fPrintAllClusters = pset.get< bool >("PrintAllClusters");
353 
354  // Consistency check
355  if(fMatchAlgs.size() > fXMatchErr.size() || fMatchAlgs.size() > fAngleMatchErr.size()
356  || fMatchAlgs.size() > fChgAsymFactor.size() || fMatchAlgs.size() > fMatchMinLen.size()
357  || fMatchAlgs.size() > fMakeAlgTracks.size()) {
358  mf::LogError("CCTM")<<"Incompatible fcl input vector sizes";
359  return;
360  }
361  // Reality check
362  for(unsigned short ii = 0; ii < fMatchAlgs.size(); ++ii) {
363  if(fAngleMatchErr[ii] <= 0 || fXMatchErr[ii] <= 0) {
364  mf::LogError("CCTM")<<"Invalid matching parameters "<<fAngleMatchErr[ii]<<" "<<fXMatchErr[ii];
365  return;
366  }
367  } // ii
368 
369  } // reconfigure
370 
371  //-------------------------------------------------
372  CCTrackMaker::~CCTrackMaker()
373  {
374  }
375 
376  //-------------------------------------------------
378  {
379  }
380 
381  /*
382  //-------------------------------------------------
383  void CCTrackMaker::endJob()
384  {
385  }
386  */
387  //------------------------------------------------------------------------------------//
388  void CCTrackMaker::produce(art::Event& evt)
389  {
390 
391  detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
392 
393  fWirePitch = geom->WirePitch();
394 
395  fChgWindow = 40; // window (ticks) for identifying shower-like clusters
396 
397  std::unique_ptr<std::vector<recob::Track>> tcol(new std::vector<recob::Track>);
398  std::unique_ptr<art::Assns<recob::Track, recob::Hit> > thassn (new art::Assns<recob::Track, recob::Hit>);
399 
400  std::unique_ptr<std::vector<recob::Vertex>> vcol(new std::vector<recob::Vertex>);
401 
402  std::unique_ptr<std::vector<recob::PFParticle>> pcol(new std::vector<recob::PFParticle>);
403 
404  std::unique_ptr< art::Assns<recob::PFParticle, recob::Track> > ptassn( new art::Assns<recob::PFParticle, recob::Track> );
405  std::unique_ptr< art::Assns<recob::PFParticle, recob::Cluster> > pcassn( new art::Assns<recob::PFParticle, recob::Cluster> );
406  std::unique_ptr< art::Assns<recob::PFParticle, recob::Seed> > psassn( new art::Assns<recob::PFParticle, recob::Seed> );
407  std::unique_ptr< art::Assns<recob::PFParticle, recob::Vertex> > pvassn( new art::Assns<recob::PFParticle, recob::Vertex> );
408 
409  // seed collection
410  std::unique_ptr<std::vector<recob::Seed>> scol(new std::vector<recob::Seed>);
411  std::unique_ptr<art::Assns<recob::Seed, recob::Hit> > shassn (new art::Assns<recob::Seed, recob::Hit>);
412 
413  // all hits
414  art::Handle< std::vector<recob::Hit> > allhitsListHandle;
415  // cluster list
416  art::Handle< std::vector<recob::Cluster> > clusterListHandle;
417  std::vector<art::Ptr<recob::Cluster>> clusterlist;
418  // ClusterCrawler Vertices
420  std::vector<art::Ptr<recob::Vertex>> vtxlist;
421 
422  // get Hits
423  allhits.clear();
424  if (evt.getByLabel(fHitModuleLabel, allhitsListHandle))
425  art::fill_ptr_vector(allhits, allhitsListHandle);
426 
427  // get Clusters
428  if (evt.getByLabel(fClusterModuleLabel, clusterListHandle))
429  art::fill_ptr_vector(clusterlist, clusterListHandle);
430  if(clusterlist.size() == 0) return;
431  // get cluster - hit associations
432  art::FindManyP<recob::Hit> fmCluHits(clusterListHandle, evt, fClusterModuleLabel);
433  // pfmCluHits.reset(something goes here);
434 
435  // get Vertices
436  if (evt.getByLabel(fVertexModuleLabel, VtxListHandle))
437  art::fill_ptr_vector(vtxlist, VtxListHandle);
438  art::FindManyP<recob::Cluster, unsigned short> fmVtxCls(VtxListHandle, evt, fVertexModuleLabel);
439 
440  std::vector<CluLen> clulens;
441 
442  unsigned short ipl, icl, end, itr, tID, tIndex;
443 
444  // maximum error (see MakeClusterChains) for considering clusters broken
445  fMaxMergeError = 30;
446  // Cut on the error for a definitely broken cluster. Clusters with fMergeErrorCut < MergeError < fMaxMergeError
447  // are possibly broken clusters but we will consider these when matching between planes
448  fMergeErrorCut = 10;
449 
450  // some junk vectors to satisfy the recob::Track constructor
451  std::vector< std::vector<double> > dQdx;
452  std::vector<double> mom(2, util::kBogusD);
453  // prepare a bogus covariance matrix so that the TrackAna module doesn't bomb
454  TMatrixD cov(5,5);
455  for(unsigned short ii = 0; ii < 5; ++ii) cov(ii, ii) = 1;
456  std::vector<TMatrixD> tmpCov;
457  tmpCov.push_back(cov);
458  tmpCov.push_back(cov);
459  std::vector< art::Ptr<recob::Hit > > tmpHits;
460  std::vector< art::Ptr<recob::Cluster > > tmpCls;
461  std::vector< art::Ptr<recob::Vertex > > tmpVtx;
462 
463  // vector for PFParticle constructor
464  std::vector<size_t> dtrIndices;
465 
466  // some vectors for recob::Seed
467  double sPos[3], sDir[3];
468  double sErr[3] = {0,0,0};
469 
470  // check consistency between clusters and associated hits
471  std::vector<art::Ptr<recob::Hit>> clusterhits;
472  for(icl = 0; icl < clusterlist.size(); ++icl) {
473  ipl = clusterlist[icl]->Plane().Plane;
474  clusterhits = fmCluHits.at(icl);
475  if(clusterhits[0]->WireID().Wire != std::nearbyint(clusterlist[icl]->EndWire())) {
476  std::cout<<"CCTM Cluster-Hit End wire mis-match "<<clusterhits[0]->WireID().Wire<<" vs "<<std::nearbyint(clusterlist[icl]->EndWire())<<" Bail out! \n";
477  return;
478  }
479  for(unsigned short iht = 0; iht < clusterhits.size(); ++iht) {
480  if(clusterhits[iht]->WireID().Plane != ipl) {
481  std::cout<<"CCTM Cluster-Hit plane mis-match "<<ipl<<" vs "<<clusterhits[iht]->WireID().Plane<<" on hit "<<iht<<" Bail out! \n";
482  return;
483  } // hit-cluster plane mis-match
484  } // iht
485  } // icl
486  // end check consistency
487 
488 // std::cout<<"************ event "<<evt.event()<<"\n";
489 
490  vtx.clear();
491  trk.clear();
492  for(cstat = 0; cstat < geom->Ncryostats(); ++cstat) {
493  for(tpc = 0; tpc < geom->Cryostat(cstat).NTPC(); ++tpc) {
494  nplanes = geom->Cryostat(cstat).TPC(tpc).Nplanes();
495  if(nplanes > 3) continue;
496  for(ipl = 0; ipl < 3; ++ipl) {
497  cls[ipl].clear();
498  clsChain[ipl].clear();
499  trkHits[ipl].clear();
500  } // ipl
501  // FillWireHitRange also calculates the charge in each plane
503  for(ipl = 0; ipl < nplanes; ++ipl) {
504  clulens.clear();
505  // sort clusters by increasing End wire number
506  for(icl = 0; icl < clusterlist.size(); ++icl) {
507  if(clusterlist[icl]->Plane().Cryostat != cstat) continue;
508  if(clusterlist[icl]->Plane().TPC != tpc) continue;
509  if(clusterlist[icl]->Plane().Plane != ipl) continue;
510  CluLen clulen;
511  clulen.index = icl;
512  clulen.length = clusterlist[icl]->EndWire();
513  clulens.push_back(clulen);
514  }
515  if(clulens.size() == 0) continue;
516  // sort clusters
517  std::sort (clulens.begin(),clulens.end(), lessThan);
518  if(clulens.size() == 0) continue;
519  for(unsigned short ii = 0; ii < clulens.size(); ++ii) {
520  const unsigned short icl = clulens[ii].index;
521  clPar clstr;
522  clstr.EvtIndex = icl;
523  recob::Cluster const& cluster = *(clusterlist[icl]);
524  // Begin info -> end index 1 (DS)
525  clstr.Wire[1] = cluster.StartWire();
526  clstr.Time[1] = cluster.StartTick();
527  clstr.X[1] = (float)detprop->ConvertTicksToX(cluster.StartTick(), ipl, tpc, cstat);
528  clstr.Angle[1] = cluster.StartAngle();
529  clstr.Slope[1] = std::tan(cluster.StartAngle());
530  clstr.Dir[1] = 0;
531 // if(fabs(clstr.Slope[1]) > 0.02) clstr.Dir[1] = -1 * (2*(clstr.Slope[1]>0)-1);
532  clstr.Charge[1] = ChgNorm[ipl] * cluster.StartCharge();
533  // this will be filled later
534  clstr.ChgNear[1] = 0;
535  clstr.VtxIndex[1] = -1;
536  clstr.mVtxIndex[1] = -1;
537  clstr.BrkIndex[1] = -1;
538  clstr.MergeError[1] = fMaxMergeError;
539  // End info -> end index 0 (US)
540  clstr.Wire[0] = cluster.EndWire();
541  clstr.Time[0] = cluster.EndTick();
542  clstr.X[0] = (float)detprop->ConvertTicksToX(cluster.EndTick(), ipl, tpc, cstat);
543  clstr.Angle[0] = cluster.EndAngle();
544  clstr.Slope[0] = std::tan(cluster.EndAngle());
545  clstr.Dir[0] = 0;
546 // if(fabs(clstr.Slope[0]) > 0.02) clstr.Dir[0] = 1 * (2*(clstr.Slope[0]>0)-1);
547  if(clstr.Time[1] > clstr.Time[0]) {
548  clstr.Dir[0] = 1; clstr.Dir[1] = -1;
549  } else {
550  clstr.Dir[0] = -1; clstr.Dir[1] = 1;
551  }
552  clstr.Charge[0] = ChgNorm[ipl] * cluster.EndCharge();
553  // this will be filled later
554  clstr.ChgNear[1] = 0;
555  clstr.VtxIndex[0] = -1;
556  clstr.mVtxIndex[0] = -1;
557  clstr.BrkIndex[0] = -1;
558  clstr.MergeError[0] = fMaxMergeError;
559  // other info
560  clstr.InTrack = -1;
561  clstr.Length = (unsigned short)(0.5 + clstr.Wire[1] - clstr.Wire[0]);
562  clstr.TotChg = ChgNorm[ipl] * cluster.Integral();
563  if(clstr.TotChg <= 0) clstr.TotChg = 1;
564  clusterhits = fmCluHits.at(icl);
565  if(clusterhits.size() == 0) {
566  mf::LogError("CCTM")<<"No associated cluster hits "<<icl;
567  continue;
568  }
569  // correct charge for missing cluster hits
570  clstr.TotChg *= clstr.Length / (float)clusterhits.size();
571  cls[ipl].push_back(clstr);
572  } // ii (icl)
573  } // ipl
574 
575 
576 // std::cout<<"FillChgNear "<<evt.event()<<"\n";
577  FillChgNear();
578 
579  // and finally the vertices
580  double xyz[3];
581  for(unsigned short ivx = 0; ivx < vtxlist.size(); ++ivx) {
582  vtxPar aVtx;
583  aVtx.ID = ivx + 1;
584  aVtx.EvtIndex = ivx;
585  vtxlist[ivx]->XYZ(xyz);
586  aVtx.X = xyz[0];
587  aVtx.Y = xyz[1];
588  aVtx.Z = xyz[2];
589  aVtx.nClusInPln[0] = 0;
590  aVtx.nClusInPln[1] = 0;
591  aVtx.nClusInPln[2] = 0;
592  std::vector<art::Ptr<recob::Cluster>> const& vtxCls = fmVtxCls.at(ivx);
593  std::vector<const unsigned short*> const& vtxClsEnd = fmVtxCls.data(ivx);
594  for(unsigned short vcass = 0; vcass < vtxCls.size(); ++vcass) {
595  icl = vtxCls[vcass].key();
596  // the cluster plane
597  ipl = vtxCls[vcass]->Plane().Plane;
598  end = *vtxClsEnd[vcass];
599  if(end > 1) throw cet::exception("CCTM")<<"Invalid end data from vertex - cluster association"<<end;
600  bool gotit = false;
601  // CCTM end is opposite of CC end
602  end = 1 - end;
603  for(unsigned short jcl = 0; jcl < cls[ipl].size(); ++jcl) {
604  if(cls[ipl][jcl].EvtIndex == icl) {
605  cls[ipl][jcl].VtxIndex[end] = ivx;
606  ++aVtx.nClusInPln[ipl];
607  gotit = true;
608  break;
609  } // index check
610  } // jcl
611  if(!gotit) throw cet::exception("CCTM")<<"Bad index from vertex - cluster association"<<icl;
612  } // icl
613  vtx.push_back(aVtx);
614  } // ivx
615  // Find broken clusters
616  MakeClusterChains(fmCluHits);
617  FindMaybeVertices();
618 
619  // call algorithms in the specified order
620  matcomb.clear();
621  for(algIndex = 0; algIndex < fMatchAlgs.size(); ++algIndex) {
622  if(fMatchAlgs[algIndex] == 1) {
623  prt = (fDebugAlg == 1);
624  VtxMatch(fmCluHits);
625  if(fMakeAlgTracks[algIndex]) SortMatches(fmCluHits, 1);
626  } else
627  if(fMatchAlgs[algIndex] == 2) {
628  prt = (fDebugAlg == 2);
629  PlnMatch(fmCluHits);
630  if(fMakeAlgTracks[algIndex]) SortMatches(fmCluHits, 2);
631  }
632  if(prt) PrintClusters();
633  } // algIndex
634  prt = false;
635  pfpToTrkID.clear();
636  // Determine the vertex/track hierarchy
637  if(fMakePFPs) {
638  TagCosmics();
639  MakeFamily();
640  }
641  FitVertices();
642 
643  // Make PFParticles -> tracks
644  for(unsigned short ipf = 0; ipf < pfpToTrkID.size(); ++ipf) {
645  // trackID of this PFP
646  tID = pfpToTrkID[ipf];
647  if(tID > trk.size()+1) {
648  mf::LogWarning("CCTM")<<"Bad track ID";
649  continue;
650  }
651  dtrIndices.clear();
652  // load the daughter PFP indices
653  mf::LogVerbatim("CCTM")<<"PFParticle "<<ipf<<" tID "<<tID;
654  for(unsigned short jpf = 0; jpf < pfpToTrkID.size(); ++jpf) {
655  itr = pfpToTrkID[jpf] - 1; // convert from track ID to track index
656  if(trk[itr].MomID == tID) dtrIndices.push_back(jpf);
657  if(trk[itr].MomID == tID) mf::LogVerbatim("CCTM")<<" dtr jpf "<<jpf<<" itr "<<itr;
658  } // jpf
659  unsigned short parentIndex = USHRT_MAX;
660  if(tID == 0) {
661  // make neutrino PFP USHRT_MAX == primary PFP
662  recob::PFParticle pfp(14, ipf, parentIndex, dtrIndices);
663  pcol->emplace_back(std::move(pfp));
664  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
665  if(!vtx[ivx].Neutrino) continue;
666  // make the vertex
667  xyz[0] = vtx[ivx].X;
668  xyz[1] = vtx[ivx].Y;
669  xyz[2] = vtx[ivx].Z;
670  size_t vStart = vcol->size();
671  recob::Vertex vertex(xyz, vtx[ivx].ID);
672  vcol->emplace_back(std::move(vertex));
673  size_t vEnd = vcol->size();
674  // PFParticle - vertex association
675  util::CreateAssn(*this, evt, *pcol, *vcol, *pvassn, vStart, vEnd);
676  vtx[ivx].ID = -vtx[ivx].ID;
677  break;
678  } // ivx
679  } else if(tID > 0){
680  // make non-neutrino PFP. Need to find the parent PFP index
681  // Track index of this PFP
682  tIndex = tID - 1;
683  // trackID of this PFP parent
684  for(unsigned short ii = 0; ii < pfpToTrkID.size(); ++ii) {
685  if(pfpToTrkID[ii] == trk[tIndex].MomID) {
686  parentIndex = ii;
687  break;
688  }
689  } // ii
690  // PFParticle
691  mf::LogVerbatim("CCTM")<<"daughters tID "<<tID<<" pdg "<<trk[tIndex].PDGCode<<" ipf "<<ipf<<" parentIndex "<<parentIndex<<" dtr size "<<dtrIndices.size();
692  recob::PFParticle pfp(trk[tIndex].PDGCode, ipf, parentIndex, dtrIndices);
693  pcol->emplace_back(std::move(pfp));
694  // track
695  // make the track
696  size_t tStart = tcol->size();
697  recob::Track track(trk[tIndex].TrjPos, trk[tIndex].TrjDir, tmpCov, dQdx, mom, tID);
698  tcol->emplace_back(std::move(track));
699  size_t tEnd = tcol->size();
700  // PFParticle - track association
701  util::CreateAssn(*this, evt, *pcol, *tcol, *ptassn, tStart, tEnd);
702  // flag this track as already put in the event
703  trk[tIndex].ID = -trk[tIndex].ID;
704  // track -> hits association
705  tmpHits.clear();
706  for(ipl = 0; ipl < nplanes; ++ipl)
707  tmpHits.insert(tmpHits.end(), trk[tIndex].TrkHits[ipl].begin(), trk[tIndex].TrkHits[ipl].end());
708  util::CreateAssn(*this, evt, *tcol, tmpHits, *thassn);
709  // Find seed hits and the end of the track that is best
710  end = 0;
711 // FindSeedHits(tIndex, end);
712  unsigned short itj = 0;
713  if(end > 0) itj = trk[tIndex].TrjPos.size() - 1;
714  for(unsigned short ii = 0; ii < 3; ++ii) {
715  sPos[ii] = trk[tIndex].TrjPos[itj](ii);
716  sDir[ii] = trk[tIndex].TrjDir[itj](ii);
717  } // ii
718  size_t sStart = scol->size();
719  recob::Seed seed(sPos, sDir, sErr, sErr);
720  scol->emplace_back(std::move(seed));
721  size_t sEnd = scol->size();
722  // PFP-seed association
723  util::CreateAssn(*this, evt, *pcol, *scol, *psassn, sStart, sEnd);
724  // Seed-hit association
725  tmpHits.clear();
726  for(ipl = 0; ipl < nplanes; ++ipl)
727  tmpHits.insert(tmpHits.end(), seedHits[ipl].begin(), seedHits[ipl].end());
728  util::CreateAssn(*this, evt, *scol, tmpHits, *shassn);
729  // cluster association
730  // PFP-cluster association
731  tmpCls.clear();
732  for(unsigned short ii = 0; ii < trk[tIndex].ClsEvtIndices.size(); ++ii) {
733  icl = trk[tIndex].ClsEvtIndices[ii];
734  tmpCls.push_back(clusterlist[icl]);
735  } // ii
736  util::CreateAssn(*this, evt, *pcol, tmpCls, *pcassn);
737  } // non-neutrino PFP
738  } // ipf
739  // make non-PFP tracks
740  for(unsigned short itr = 0; itr < trk.size(); ++itr) {
741  // ignore already saved tracks
742  if(trk[itr].ID < 0) continue;
743  recob::Track track(trk[itr].TrjPos, trk[itr].TrjDir, tmpCov, dQdx, mom, trk[itr].ID);
744  tcol->emplace_back(std::move(track));
745  tmpHits.clear();
746  for(ipl = 0; ipl < nplanes; ++ipl)
747  tmpHits.insert(tmpHits.end(), trk[itr].TrkHits[ipl].begin(), trk[itr].TrkHits[ipl].end());
748  util::CreateAssn(*this, evt, *tcol, tmpHits, *thassn);
749  } // itr
750  // make remnant vertices
751  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
752  if(vtx[ivx].ID < 0) continue;
753  xyz[0] = vtx[ivx].X;
754  xyz[1] = vtx[ivx].Y;
755  xyz[2] = vtx[ivx].Z;
756  recob::Vertex vertex(xyz, vtx[ivx].ID);
757  vcol->emplace_back(std::move(vertex));
758  }
759  if(fDebugAlg > 0) PrintTracks();
760 
761  double orphanLen = 0;
762  for(ipl = 0; ipl < nplanes; ++ipl) {
763  for(icl = 0; icl < cls[ipl].size(); ++icl) {
764  if(cls[ipl][icl].Length > 40 && cls[ipl][icl].InTrack < 0) {
765  orphanLen += cls[ipl][icl].Length;
766  // unused cluster
767  mf::LogVerbatim("CCTM")<<"Orphan long cluster "<<ipl<<":"<<icl<<":"<<cls[ipl][icl].Wire[0]
768  <<":"<<(int)cls[ipl][icl].Time[0]<<" length "<<cls[ipl][icl].Length;
769  }
770  } // icl
771 
772  cls[ipl].clear();
773  clsChain[ipl].clear();
774  } // ipl
775  std::cout<<"Total orphan length "<<orphanLen<<"\n";
776  trkHits[ipl].clear();
777  seedHits[ipl].clear();
778  vxCls[ipl].clear();
779  } // tpc
780  } // cstat
781 
782  evt.put(std::move(pcol));
783  evt.put(std::move(ptassn));
784  evt.put(std::move(pcassn));
785  evt.put(std::move(pvassn));
786  evt.put(std::move(psassn));
787  evt.put(std::move(tcol));
788  evt.put(std::move(thassn));
789  evt.put(std::move(scol));
790  evt.put(std::move(vcol));
791 
792  // final cleanup
793  vtx.clear();
794  trk.clear();
795  allhits.clear();
796  matcomb.clear();
797  pfpToTrkID.clear();
798 
799 
800  } // produce
801 
803 /*
804  void CCTrackMaker::FindSeedHits(unsigned short itk, unsigned short& end)
805  {
806  // Returns a subset of track hits that are near the start position and form
807  // a decently fit line
808 
809  unsigned short ipl;
810  for(ipl = 0; ipl < 3; ++ipl) seedHits[ipl].clear();
811 
812  unsigned short maxLen = 0, minLen = USHRT_MAX, midLen = 0;
813  for(unsigned short tEnd = 0; tEnd < 2; ++tEnd) {
814  if(!trk[itk].GoodEnd[tEnd]) continue;
815  for(ipl = 0; ipl < nplanes; ++ipl) {
816  if(trkHits[ipl].size() > maxLen) maxLen = trkHits[ipl].size();
817  if(trkHits[ipl].size() < minLen) minLen = trkHits[ipl].size();
818  }
819  // Return all track hits if it is short
820  if(maxLen < 10) {
821  for(ipl = 0; ipl < nplanes; ++ipl) seedHits[ipl] = trkHits[ipl];
822  end = tEnd;
823  return;
824  } // short track
825  // start a seed using 2 hits in the "middle length" plane
826  unsigned short midLenPln = 0;
827  for(ipl = 0; ipl < nplanes; ++ipl) {
828  if(trkHits[ipl].size() >= minLen && trkHits[ipl].size() <= maxLen) {
829  midLenPln = ipl;
830  midLen = trkHits[ipl].size();
831  break;
832  }
833  } // ipl
834  // ratio of hit lengths in each plane
835  std::array<float, 3> hitRat;
836  for(ipl = 0; ipl < nplanes; ++ipl) hitRat[ipl] = (float)trkHits[ipl].size() / float(midLen);
837  // working vectors passed to TrackLineFitAlg
838  std::vector<geo::WireID> hitWID;
839  std::vector<double> hitX;
840  std::vector<double> hitXErr;
841  TVector3 xyz, dir;
842  float ChiDOF;
843  double xOrigin = TrjPos[tEnd](0);
844  fTrackLineFitAlg.TrkLineFit(hitWID, hitX, hitXErr, xOrigin, xyz, dir, ChiDOF);
845 
846  end = tEnd;
847  return;
848  } // tend
849 
850  } // FindSeedHits
851 */
852 
854  void CCTrackMaker::FitVertices()
855  {
856 
857  std::vector<std::vector<geo::WireID>> hitWID;
858  std::vector<std::vector<double>> hitX;
859  std::vector<std::vector<double>> hitXErr;
860  TVector3 pos, posErr;
861  std::vector<TVector3> trkDir;
862  std::vector<TVector3> trkDirErr;
863  float ChiDOF;
864 
865  if(fNVtxTrkHitsFit == 0) return;
866 
867  unsigned short indx, indx2, iht, nHitsFit;
868 
869 // mf::LogVerbatim("CCTM")<<"Inside FitVertices "<<vtx.size()<<" trks "<<trk.size()<<"\n";
870 
871  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
872  if(!vtx[ivx].Neutrino) continue;
873  hitWID.clear();
874  hitX.clear();
875  hitXErr.clear();
876  trkDir.clear();
877  // find the tracks associated with this vertex
878  unsigned int thePln, theTPC, theCst;
879  for(unsigned short itk = 0; itk < trk.size(); ++itk) {
880  for(unsigned short end = 0; end < 2; ++end) {
881  if(trk[itk].VtxIndex[end] != ivx) continue;
882  unsigned short itj = 0;
883  if(end == 1) itj = trk[itk].TrjPos.size() - 1;
884 // mf::LogVerbatim("CCTM")<<"ivx "<<ivx<<" trk "<<itk<<" end "<<end<<" TrjPos "<<trk[itk].TrjPos[itj](0)<<" "<<trk[itk].TrjPos[itj](1)<<" "
885 // <<trk[itk].TrjPos[itj](2)<<" TrjDir "<<trk[itk].TrjDir[itj](0)<<" "<<trk[itk].TrjDir[itj](1)<<" "<<trk[itk].TrjDir[itj](2);
886  // increase the size of the hit vectors by 1 for this track
887  indx = hitX.size();
888  hitWID.resize(indx + 1);
889  hitX.resize(indx + 1);
890  hitXErr.resize(indx + 1);
891  trkDir.resize(indx + 1);
892  trkDir[indx] = trk[itk].TrjDir[itj];
893  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
894  if(trk[itk].TrkHits[ipl].size() < 2) continue;
895  // make slots for the hits on this track in this plane
896  nHitsFit = trk[itk].TrkHits[ipl].size();
897  if(nHitsFit > fNVtxTrkHitsFit) nHitsFit = fNVtxTrkHitsFit;
898  indx2 = hitWID[indx].size();
899  hitWID[indx].resize(indx2 + nHitsFit);
900  hitX[indx].resize(indx2 + nHitsFit);
901  hitXErr[indx].resize(indx2 + nHitsFit);
902  for(unsigned short ii = 0; ii < nHitsFit; ++ii) {
903  if(end == 0) {
904  iht = ii;
905  } else {
906  iht = trk[itk].TrkHits[ipl].size() - ii - 1;
907  }
908  hitWID[indx][indx2 + ii] = trk[itk].TrkHits[ipl][iht]->WireID();
909  thePln = trk[itk].TrkHits[ipl][iht]->WireID().Plane;
910  theTPC = trk[itk].TrkHits[ipl][iht]->WireID().TPC;
911  theCst = trk[itk].TrkHits[ipl][iht]->WireID().Cryostat;
912  hitX[indx][indx2 + ii] = detprop->ConvertTicksToX(trk[itk].TrkHits[ipl][iht]->PeakTime(), thePln, theTPC, theCst);
913  hitXErr[indx][indx2 + ii] = fHitFitErrFac * trk[itk].TrkHits[ipl][iht]->RMS();
914 // mf::LogVerbatim("CCTM")<<"CCTM "<<itk<<" indx "<<indx<<" ii "<<ii<<" WID "<<trk[itk].TrkHits[ipl][iht]->WireID()
915 // <<" X "<<hitX[indx][indx2 + ii]<<" XErr "<<hitXErr[indx][indx2 + ii]<<" RMS "<<trk[itk].TrkHits[ipl][iht]->RMS()
916 // <<" SigmaPeakTime "<<trk[itk].TrkHits[ipl][iht]->SigmaPeakTime();
917  } // ii
918  } // ipl
919  } // end
920  } // itk
921  if(hitX.size() < 2) {
922  mf::LogVerbatim("CCTM")<<"Not enough hits to fit vtx "<<ivx;
923  continue;
924  } // hitX.size() < 2
925  pos(0) = vtx[ivx].X;
926  pos(1) = vtx[ivx].Y;
927  pos(2) = vtx[ivx].Z;
928  fVertexFitAlg.VertexFit(hitWID, hitX, hitXErr, pos, posErr, trkDir, trkDirErr, ChiDOF);
929 /*
930  mf::LogVerbatim("CCTM")<<"FV: CC Vtx pos "<<vtx[ivx].X<<" "<<vtx[ivx].Y<<" "<<vtx[ivx].Z;
931  mf::LogVerbatim("CCTM")<<"FV: Fitted "<<pos[0]<<" "<<pos[1]<<" "<<pos[2]<<" ChiDOF "<<ChiDOF;
932  mf::LogVerbatim("CCTM")<<"FV: Errors "<<posErr[0]<<" "<<posErr[1]<<" "<<posErr[2];
933 */
934  if(ChiDOF > 3000) continue;
935  // update the vertex position
936  vtx[ivx].X = pos(0);
937  vtx[ivx].Y = pos(1);
938  vtx[ivx].Z = pos(2);
939  // and the track trajectory
940  unsigned short fitTrk = 0;
941  for(unsigned short itk = 0; itk < trk.size(); ++itk) {
942  for(unsigned short end = 0; end < 2; ++end) {
943  if(trk[itk].VtxIndex[end] != ivx) continue;
944  unsigned short itj = 0;
945  if(end == 1) itj = trk[itk].TrjPos.size() - 1;
946  trk[itk].TrjDir[itj] = trkDir[fitTrk];
947  ++fitTrk;
948  } // end
949  } // itk
950  } // ivx
951  } // FitVertices
952 
954  void CCTrackMaker::FillChgNear()
955  {
956  // fills the CHgNear array
957 
958  unsigned short wire, nwires, indx;
959  float dir, ctime, cx, chgWinLo, chgWinHi;
960  float cnear;
961 
962  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
963  for(unsigned short icl = 0; icl < cls[ipl].size(); ++icl) {
964  // find the nearby charge at the US and DS ends
965  nwires = cls[ipl][icl].Length / 2;
966  if(nwires < 2) continue;
967  // maximum of 30 wires for long clusters
968  if(nwires > 30) nwires = 30;
969  for(unsigned short end = 0; end < 2; ++end) {
970  cnear = 0;
971  // direction for adding/subtracting wire numbers
972  dir = 1 - 2 * end;
973  for(unsigned short w = 0; w < nwires; ++w) {
974  wire = cls[ipl][icl].Wire[end] + dir * w;
975  cx = cls[ipl][icl].X[end] + dir * w * cls[ipl][icl].Slope[end] * fWirePitch;
976  ctime = detprop->ConvertXToTicks(cx, ipl, tpc, cstat);
977  chgWinLo = ctime - fChgWindow;
978  chgWinHi = ctime + fChgWindow;
979  indx = wire - firstWire[ipl];
980  if(WireHitRange[ipl][indx].first < 0) continue;
981  unsigned int firhit = WireHitRange[ipl][indx].first;
982  unsigned int lashit = WireHitRange[ipl][indx].second;
983  for(unsigned int hit = firhit; hit < lashit; ++hit) {
984  if(hit > allhits.size() - 1) {
985  mf::LogError("CCTM")<<"FillChgNear bad lashit "<<lashit<<" size "<<allhits.size()<<"\n";
986  continue;
987  }
988  if(allhits[hit]->PeakTime() < chgWinLo) continue;
989  if(allhits[hit]->PeakTime() > chgWinHi) continue;
990  cnear += allhits[hit]->Integral();
991  } // hit
992  } // w
993  cnear /= (float)(nwires-1);
994  if(cnear > cls[ipl][icl].Charge[end]) {
995  cls[ipl][icl].ChgNear[end] = ChgNorm[ipl] * cnear / cls[ipl][icl].Charge[end];
996  } else {
997  cls[ipl][icl].ChgNear[end] = 0;
998  }
999  } // end
1000  } // icl
1001  } //ipl
1002 
1003  } // FillChgNear
1004 
1005 
1007  void CCTrackMaker::MakeFamily()
1008  {
1009  // define the track/vertex and mother/daughter relationships
1010 
1011  unsigned short ivx, itr, ipl, ii, jtr;
1012  unsigned short nus, nds, nuhs, ndhs;
1013  float longUSTrk, longDSTrk, qual;
1014 
1015  // min distance^2 between a neutrino vertex candidate and a through
1016  // going muon
1017  float tgMuonCut2 = 9;
1018 
1019  // struct for neutrino vertex candidates
1020  struct NuVtx {
1021  unsigned short VtxIndex;
1022  unsigned short nUSTk;
1023  unsigned short nDSTk;
1024  unsigned short nUSHit;
1025  unsigned short nDSHit;
1026  float longUSTrk;
1027  float longDSTrk;
1028  float Qual;
1029  };
1030  std::vector<NuVtx> nuVtxCand;
1031 
1032  NuVtx aNuVtx;
1033 
1034  // analyze all of the vertices
1035  float best = 999, dx, dy, dz, dr;
1036  short imbest = -1;
1037  bool skipVtx;
1038  unsigned short itj;
1039  for(ivx = 0; ivx < vtx.size(); ++ivx) {
1040  vtx[ivx].Neutrino = false;
1041  nus = 0; nds = 0; nuhs = 0; ndhs = 0;
1042  longUSTrk = 0; longDSTrk = 0;
1043  skipVtx = false;
1044  // skip vertices that are close to through-going muons
1045  for(itr = 0; itr < trk.size(); ++itr) {
1046  if(trk[itr].ID < 0) continue;
1047  if(trk[itr].PDGCode != 13) continue;
1048  for(itj = 0; itj < trk[itr].TrjPos.size(); ++itj) {
1049  dx = trk[itr].TrjPos[itj](0) - vtx[ivx].X;
1050  dy = trk[itr].TrjPos[itj](1) - vtx[ivx].Y;
1051  dz = trk[itr].TrjPos[itj](2) - vtx[ivx].Z;
1052  dr = dx * dx + dy * dy + dz * dz;
1053  if(dr < tgMuonCut2) {
1054  skipVtx = true;
1055  break;
1056  }
1057  if(skipVtx) break;
1058  } // itj
1059  if(skipVtx) break;
1060  } // itr
1061  if(skipVtx) continue;
1062  for(itr = 0; itr < trk.size(); ++itr) {
1063  if(trk[itr].ID < 0) continue;
1064  if(trk[itr].VtxIndex[0] == ivx) {
1065  // DS-going track
1066  ++nds;
1067  if(trk[itr].Length > longDSTrk) longDSTrk = trk[itr].Length;
1068  for(ipl = 0; ipl < nplanes; ++ipl) ndhs += trk[itr].TrkHits[ipl].size();
1069 // std::cout<<"MakeFamily: ivx "<<ivx<<" DS itr "<<trk[itr].ID<<" len "<<trk[itr].Length<<"\n";
1070  } // DS-going track
1071  // Reject this vertex as a neutrino candidate if the track being
1072  // considered has a starting vertex
1073  if(trk[itr].VtxIndex[1] == ivx && trk[itr].VtxIndex[0] >= 0) {
1074  skipVtx = true;
1075  break;
1076  } // trk[itr].VtxIndex[0] > 0
1077  if(trk[itr].VtxIndex[1] == ivx && trk[itr].VtxIndex[0] < 0) {
1078  // US-going track w no US vertex
1079  ++nus;
1080  if(trk[itr].Length > longUSTrk) longUSTrk = trk[itr].Length;
1081  for(ipl = 0; ipl < nplanes; ++ipl) nuhs += trk[itr].TrkHits[ipl].size();
1082 // std::cout<<"MakeFamily: ivx "<<ivx<<" US itr "<<trk[itr].ID<<" len "<<trk[itr].Length<<"\n";
1083  } // US-going track
1084  } // itr
1085  if(skipVtx) continue;
1086  if(nds == 0) continue;
1087  qual = 1 / (float)nds;
1088  qual /= (float)ndhs;
1089  if(nus > 0) qual *= (float)nuhs / (float)ndhs;
1090  if(qual < best) {
1091  best = qual;
1092  imbest = ivx;
1093  }
1094  if(nds > 0 && longDSTrk > 5) {
1095  // at least one longish track going DS
1096  aNuVtx.VtxIndex = ivx;
1097  aNuVtx.nUSTk = nus;
1098  aNuVtx.nDSTk = nds;
1099  aNuVtx.nUSHit = nuhs;
1100  aNuVtx.nDSHit = ndhs;
1101  aNuVtx.longUSTrk = longUSTrk;
1102  aNuVtx.longDSTrk = longDSTrk;
1103  aNuVtx.Qual = qual;
1104  nuVtxCand.push_back(aNuVtx);
1105  }
1106  } // ivx
1107 /*
1108  mf::LogVerbatim("CCTM")<<"Neutrino vtx candidates";
1109  for(unsigned short ican = 0; ican < nuVtxCand.size(); ++ican)
1110  mf::LogVerbatim("CCTM")<<"Can "<<ican<<" vtx "<<nuVtxCand[ican].VtxIndex<<" nUSTk "<<nuVtxCand[ican].nUSTk
1111  <<" nUSHit "<<nuVtxCand[ican].nUSHit<<" longUS "<<nuVtxCand[ican].longUSTrk<<" nDSTk "<<nuVtxCand[ican].nDSTk
1112  <<" nDSHit "<<nuVtxCand[ican].nDSHit<<" longDS "<<nuVtxCand[ican].longDSTrk<<" Qual "<<nuVtxCand[ican].Qual;
1113 */
1114  if(imbest < 0) return;
1115 
1116  // Found the neutrino interaction vertex
1117  ivx = imbest;
1118  vtx[ivx].Neutrino = true;
1119  // Put a 0 into the PFParticle -> track vector to identify a neutrino
1120  // track with no trajectory or hits
1121  pfpToTrkID.push_back(0);
1122 
1123  // list of DS-going current generation daughters so we can fix next
1124  // generation daughter assignments. This code section assumes that there
1125  // are no decays or 2ndry interactions with US-going primary tracks.
1126  std::vector<unsigned short> dtrGen;
1127  std::vector<unsigned short> dtrNextGen;
1128  for(itr = 0; itr < trk.size(); ++itr) {
1129  if(trk[itr].ID < 0) continue;
1130  if(trk[itr].VtxIndex[0] == ivx) {
1131  // DS-going primary track
1132  trk[itr].MomID = 0;
1133  // call every track coming from a neutrino vertex a proton
1134  trk[itr].PDGCode = 2212;
1135  pfpToTrkID.push_back(trk[itr].ID);
1136  dtrGen.push_back(itr);
1137  } // DS-going primary track
1138  if(trk[itr].VtxIndex[1] == ivx) {
1139  // US-going primary track
1140  trk[itr].MomID = 0;
1141  // call every track coming from a neutrino vertex a proton
1142  trk[itr].PDGCode = 2212;
1143  pfpToTrkID.push_back(trk[itr].ID);
1144  // reverse the track trajectory
1145  std::reverse(trk[itr].TrjPos.begin(), trk[itr].TrjPos.end());
1146  for(ii = 0; ii < trk[itr].TrjDir.size(); ++ii)
1147  trk[itr].TrjDir[ii] = -trk[itr].TrjDir[ii];
1148  } // DS-going primary track
1149  } // itr
1150 
1151  if(dtrGen.size() == 0) return;
1152 
1153  unsigned short tmp, indx;
1154  unsigned short nit = 0;
1155 
1156  // follow daughters through all generations (< 10). Daughter tracks
1157  // may go US or DS
1158  while(nit < 10) {
1159  ++nit;
1160  dtrNextGen.clear();
1161  // Look for next generation daughters
1162  for(ii = 0; ii < dtrGen.size(); ++ii) {
1163  itr = dtrGen[ii];
1164  if(trk[itr].VtxIndex[1] >= 0) {
1165  // found a DS vertex
1166  ivx = trk[itr].VtxIndex[1];
1167  // look for a track associated with this vertex
1168  for(jtr = 0; jtr < trk.size(); ++jtr) {
1169  if(jtr == itr) continue;
1170  if(trk[jtr].VtxIndex[0] == ivx) {
1171  // DS-going track
1172  indx = trk[itr].DtrID.size();
1173  trk[itr].DtrID.resize(indx + 1);
1174  trk[itr].DtrID[indx] = jtr;
1175 // std::cout<<"itr "<<itr<<" dtr "<<jtr<<" DtrID size "<<trk[itr].DtrID.size()<<"\n";
1176  trk[jtr].MomID = trk[itr].ID;
1177  // call all daughters pions
1178  trk[jtr].PDGCode = 211;
1179  dtrNextGen.push_back(jtr);
1180  pfpToTrkID.push_back(trk[jtr].ID);
1181  } // DS-going track
1182  if(trk[jtr].VtxIndex[1] == ivx) {
1183  // US-going track
1184  indx = trk[itr].DtrID.size();
1185  trk[itr].DtrID.resize(indx + 1);
1186  trk[itr].DtrID[indx] = jtr;
1187 // std::cout<<"itr "<<itr<<" dtr "<<jtr<<" DtrID size "<<trk[itr].DtrID.size()<<"\n";
1188  trk[jtr].MomID = trk[itr].ID;
1189  // call all daughters pions
1190  trk[jtr].PDGCode = 211;
1191  dtrNextGen.push_back(jtr);
1192  pfpToTrkID.push_back(trk[jtr].ID);
1193  // reverse the track trajectory
1194  std::reverse(trk[jtr].TrjPos.begin(), trk[jtr].TrjPos.end());
1195  for(unsigned short jj = 0; jj < trk[jtr].TrjDir.size(); ++jj)
1196  trk[jtr].TrjDir[jj] = -trk[jtr].TrjDir[jj];
1197  // interchange the trk - vtx assignments
1198  tmp = trk[jtr].VtxIndex[0];
1199  trk[jtr].VtxIndex[0] = trk[jtr].VtxIndex[1];
1200  trk[jtr].VtxIndex[1] = tmp;
1201  } // DS-going track
1202  } // jtr
1203  } // trk[itr].VtxIndex[0] >= 0
1204  } // ii (itr)
1205  // break out if no next gen daughters found
1206  if(dtrNextGen.size() == 0) break;
1207  dtrGen = dtrNextGen;
1208  } // nit
1209 
1210  } // MakeFamily
1211 
1213  void CCTrackMaker::TagCosmics()
1214  {
1215  // Make cosmic ray PFParticles
1216  unsigned short ipf, itj;
1217  bool skipit = true;
1218 
1219  // Y,Z limits of the detector
1220  double local[3] = {0.,0.,0.};
1221  double world[3] = {0.,0.,0.};
1222 
1223  const geo::TPCGeo &thetpc = geom->TPC(tpc, cstat);
1224  thetpc.LocalToWorld(local,world);
1225  float XLo = world[0] - geom->DetHalfWidth(tpc,cstat) + fFiducialCut;
1226  float XHi = world[0] + geom->DetHalfWidth(tpc,cstat) - fFiducialCut;
1227  float YLo = world[1] - geom->DetHalfHeight(tpc,cstat) + fFiducialCut;
1228  float YHi = world[1] + geom->DetHalfHeight(tpc,cstat) - fFiducialCut;
1229  float ZLo = world[2] - geom->DetLength(tpc,cstat)/2 + fFiducialCut;
1230  float ZHi = world[2] + geom->DetLength(tpc,cstat)/2 - fFiducialCut;
1231 
1232  bool startsIn, endsIn;
1233 
1234  for(unsigned short itk = 0; itk < trk.size(); ++itk) {
1235  // ignore already used tracks
1236  if(trk[itk].ID < 0) continue;
1237  // ignore short tracks (< 10 cm)
1238  if(trk[itk].Length < 10) continue;
1239  // check for already identified PFPs
1240  skipit = false;
1241  for(ipf = 0; ipf < pfpToTrkID.size(); ++ipf) {
1242  if(pfpToTrkID[ipf] == trk[itk].ID) {
1243  skipit = true;
1244  break;
1245  }
1246  } // ipf
1247  if(skipit) continue;
1248  startsIn = true;
1249  if(trk[itk].TrjPos[0](0) < XLo || trk[itk].TrjPos[0](0) > XHi) startsIn = false;
1250  if(trk[itk].TrjPos[0](1) < YLo || trk[itk].TrjPos[0](1) > YHi) startsIn = false;
1251  if(trk[itk].TrjPos[0](2) < ZLo || trk[itk].TrjPos[0](2) > ZHi) startsIn = false;
1252 // std::cout<<"Trk "<<trk[itk].ID<<" X0 "<<(int)trk[itk].TrjPos[0](0)<<" Y0 "<<(int)trk[itk].TrjPos[0](1)<<" Z0 "<<(int)trk[itk].TrjPos[0](2)<<" startsIn "<<startsIn<<"\n";
1253  if(startsIn) continue;
1254  endsIn = true;
1255  itj = trk[itk].TrjPos.size() - 1;
1256  if(trk[itk].TrjPos[itj](0) < XLo || trk[itk].TrjPos[itj](0) > XHi) endsIn = false;
1257  if(trk[itk].TrjPos[itj](1) < YLo || trk[itk].TrjPos[itj](1) > YHi) endsIn = false;
1258  if(trk[itk].TrjPos[itj](2) < ZLo || trk[itk].TrjPos[itj](2) > ZHi) endsIn = false;
1259 // std::cout<<" X1 "<<(int)trk[itk].TrjPos[itj](0)<<" Y1 "<<(int)trk[itk].TrjPos[itj](1)<<" Z1 "<<(int)trk[itk].TrjPos[itj](2)<<" endsIn "<<endsIn<<"\n";
1260  if(endsIn) continue;
1261  // call it a cosmic muon
1262  trk[itk].PDGCode = 13;
1263  pfpToTrkID.push_back(trk[itk].ID);
1264  } // itk
1265 
1266  if(fDeltaRayCut <= 0) return;
1267 
1268  for(unsigned short itk = 0; itk < trk.size(); ++itk) {
1269  // find a tagged cosmic ray
1270  if(trk[itk].PDGCode != 13) continue;
1271 
1272  } // itk
1273 
1274  } // TagCosmics
1275 
1277  void CCTrackMaker::VtxMatch(art::FindManyP<recob::Hit> const& fmCluHits)
1278  {
1279  // Use vertex assignments to match clusters
1280  unsigned short ivx, ii, ipl, icl, jj, jpl, jcl, kk, kpl, kcl;
1281  short idir, iend, jdir, jend, kdir, kend, ioend;
1282 
1283  for(ivx = 0; ivx < vtx.size(); ++ivx) {
1284 
1285  // list of cluster chains associated with this vertex in each plane
1286  for(ipl = 0; ipl < nplanes; ++ipl) {
1287  vxCls[ipl].clear();
1288  for(icl = 0; icl < clsChain[ipl].size(); ++icl) {
1289  if(clsChain[ipl][icl].InTrack >= 0) continue;
1290  for(iend = 0; iend < 2; ++iend) {
1291  if(clsChain[ipl][icl].VtxIndex[iend] == vtx[ivx].EvtIndex) vxCls[ipl].push_back(icl);
1292  } // end
1293  } // icl
1294  } // ipl
1295 
1296  if(prt) {
1297  mf::LogVerbatim myprt("CCTM");
1298  myprt<<"VtxMatch: Vertex ID "<<vtx[ivx].EvtIndex<<"\n";
1299  for(ipl = 0; ipl < nplanes; ++ipl) {
1300  myprt<<"ipl "<<ipl<<" cls";
1301  for(unsigned short ii = 0; ii < vxCls[ipl].size(); ++ii) myprt<<" "<<vxCls[ipl][ii];
1302  myprt<<"\n";
1303  } // ipl
1304  } // prt
1305  // match between planes, requiring clusters matched to this vertex
1306  iend = 0; jend = 0;
1307  bool gotkcl;
1308  float totErr;
1309  for(ipl = 0; ipl < nplanes; ++ipl) {
1310  if(nplanes == 2 && ipl > 0) continue;
1311  for(ii = 0; ii < vxCls[ipl].size(); ++ii) {
1312  icl = vxCls[ipl][ii];
1313  // ignore used clusters
1314  if(clsChain[ipl][icl].InTrack >= 0) continue;
1315  jpl = (ipl + 1) % nplanes;
1316  kpl = (jpl + 1) % nplanes;
1317  for(jj = 0; jj < vxCls[jpl].size(); ++jj) {
1318  jcl = vxCls[jpl][jj];
1319  if(clsChain[jpl][jcl].InTrack >= 0) continue;
1320  for(iend = 0; iend < 2; ++iend) {
1321  if(clsChain[ipl][icl].VtxIndex[iend] != vtx[ivx].EvtIndex) continue;
1322  ioend = 1 - iend;
1323  idir = clsChain[ipl][icl].Dir[iend];
1324  for(jend = 0; jend < 2; ++jend) {
1325  if(clsChain[jpl][jcl].VtxIndex[jend] != vtx[ivx].EvtIndex) continue;
1326  jdir = clsChain[jpl][jcl].Dir[jend];
1327  if(idir != 0 && jdir != 0 && idir != jdir) continue;
1328  // ignore outrageously bad other end X matches
1329  if(fabs(clsChain[jpl][jcl].X[1 - jend] - clsChain[ipl][icl].X[ioend]) > 50) continue;
1330  MatchPars match;
1331  match.Cls[ipl] = icl; match.End[ipl] = iend;
1332  match.Cls[jpl] = jcl; match.End[jpl] = jend;
1333  match.Vtx = ivx; match.oVtx = -1;
1334  // set large so that DupMatch doesn't get confused when called before FillEndMatch
1335  match.Err = 1E6; match.oErr = 1E6;
1336  if(nplanes == 2) {
1337 // mf::LogVerbatim("CCTM")<<"chk "<<ipl<<":"<<match.Cls[ipl]<<":"<<match.End[ipl]<<" and "<<jpl<<":"<<match.Cls[jpl]<<":"<<match.End[jpl];
1338  FillEndMatch2(match);
1339  if(prt) mf::LogVerbatim("CCTM")<<"FillEndMatch2: Err "<<match.Err<<" oErr "<<match.oErr;
1340  if(match.Err + match.oErr > 100) continue;
1341  if(DupMatch(match)) continue;
1342  matcomb.push_back(match);
1343  continue;
1344  }
1345  match.Cls[kpl] = -1; match.End[kpl] = 0;
1346  if(prt) mf::LogVerbatim("CCTM")<<"VtxMatch: check "<<ipl<<":"<<icl<<":"<<iend<<" and "<<jpl<<":"<<jcl<<":"<<jend<<" for cluster in kpl "<<kpl;
1347  gotkcl = false;
1348  for(kk = 0; kk < vxCls[kpl].size(); ++kk) {
1349  kcl = vxCls[kpl][kk];
1350  if(clsChain[kpl][kcl].InTrack >= 0) continue;
1351  for(kend = 0; kend < 2; ++kend) {
1352  kdir = clsChain[kpl][kcl].Dir[kend];
1353  if(idir != 0 && kdir != 0 && idir != kdir) continue;
1354  if(clsChain[kpl][kcl].VtxIndex[kend] != vtx[ivx].EvtIndex) continue;
1355  // rough check of other end match
1356  // TODO: SHOWER-LIKE CLUSTER CHECK
1357  match.Cls[kpl] = kcl; match.End[kpl] = kend;
1358  // first call to ignore redundant matches
1359  if(DupMatch(match)) continue;
1360  FillEndMatch(match);
1361 // mf::LogVerbatim("CCTM")<<" Chg "<<match.Chg[kpl]<<" Err "<<match.Err<<" oErr "<<match.oErr;
1362  // ignore if no signal at the other end
1363  if(match.Chg[kpl] <= 0) continue;
1364  if(match.Err + match.oErr > 100) continue;
1365  // second call to keep matches with better error
1366  if(DupMatch(match)) continue;
1367  matcomb.push_back(match);
1368  gotkcl = true;
1369 // break;
1370  } // kend
1371  } // kk -> kcl
1372  if(gotkcl) continue;
1373  // look for a cluster that missed the vertex assignment
1374  float best = 10;
1375  short kbst = -1;
1376  unsigned short kbend = 0;
1377  if(prt) mf::LogVerbatim("CCTM")<<"VtxMatch: look for missed cluster chain in kpl";
1378  for(kcl = 0; kcl < clsChain[kpl].size(); ++kcl) {
1379  if(clsChain[kpl][kcl].InTrack >= 0) continue;
1380  for(kend = 0; kend < 2; ++kend) {
1381  kdir = clsChain[kpl][kcl].Dir[kend];
1382  if(idir != 0 && kdir != 0 && idir != kdir) continue;
1383  if(clsChain[kpl][kcl].VtxIndex[kend] >= 0) continue;
1384  // make a rough dX cut at the match end
1385  if(fabs(clsChain[kpl][kcl].X[kend] - vtx[ivx].X) > 5) continue;
1386  // and at the other end
1387  if(fabs(clsChain[kpl][kcl].X[1 - kend] - clsChain[ipl][icl].X[ioend]) > 50) continue;
1388  // check the error
1389  match.Cls[kpl] = kcl; match.End[kpl] = kend;
1390  if(DupMatch(match)) continue;
1391  FillEndMatch(match);
1392  totErr = match.Err + match.oErr;
1393  if(prt) {
1394  mf::LogVerbatim myprt("CCTM");
1395  myprt<<"VtxMatch: Chk missing cluster match ";
1396  for(unsigned short ii = 0; ii < nplanes; ++ii)
1397  myprt<<" "<<ii<<":"<<match.Cls[ii]<<":"<<match.End[ii];
1398  myprt<<" Err "<<match.Err<<"\n";
1399  }
1400  if(totErr > 100) continue;
1401  if(totErr < best) {
1402  best = totErr;
1403  kbst = kcl;
1404  kbend = kend;
1405  }
1406  } // kend
1407  } // kcl
1408  if(kbst >= 0) {
1409  // found a decent match
1410  match.Cls[kpl] = kbst; match.End[kpl] = kbend;
1411  FillEndMatch(match);
1412  matcomb.push_back(match);
1413  // assign the vertex to this cluster
1414  clsChain[kpl][kbst].VtxIndex[kbend] = ivx;
1415  // and update vxCls
1416  vxCls[kpl].push_back(kbst);
1417  } else {
1418  // Try a 2 plane match if a 3 plane match didn't work
1419  match.Cls[kpl] = -1; match.End[kpl] = 0;
1420  if(DupMatch(match)) continue;
1421  FillEndMatch(match);
1422  if(match.Err + match.oErr < 100) matcomb.push_back(match);
1423  }
1424  } // jend
1425  } // iend
1426  } // jj
1427  } // ii -> icl
1428  } // ipl
1429 
1430  if(matcomb.size() == 0) continue;
1431  SortMatches(fmCluHits, 1);
1432 
1433  } // ivx
1434 
1435  for(ipl = 0; ipl < 3; ++ipl) vxCls[ipl].clear();
1436 
1437  } // VtxMatch
1438 
1440  void CCTrackMaker::FindMaybeVertices()
1441  {
1442  // Project clusters to vertices and fill mVtxIndex. No requirement is
1443  // made that charge exists on the line between the Begin (End) of the
1444  // cluster and the vertex
1445  unsigned short ipl, icl, end, ivx, oend;
1446  float best, dWire, dX;
1447  short ibstvx;
1448 
1449  if(vtx.size() == 0) return;
1450 
1451  for(ipl = 0; ipl < nplanes; ++ipl) {
1452  for(icl = 0; icl < cls[ipl].size(); ++icl) {
1453  for(end = 0; end < 2; ++end) {
1454  // ignore already attached clusters
1455  if(cls[ipl][icl].VtxIndex[end] >= 0) continue;
1456  ibstvx = -1;
1457  best = 1.;
1458  // index of the other end
1459  oend = 1 - end;
1460  for(ivx = 0; ivx < vtx.size(); ++ ivx) {
1461  // ignore if the other end is attached to this vertex (which can happen with short clusters)
1462  if(cls[ipl][icl].VtxIndex[oend] == ivx) continue;
1463  dWire = geom->WireCoordinate(vtx[ivx].Y, vtx[ivx].Z, ipl, tpc, cstat) - cls[ipl][icl].Wire[end];
1464  /*
1465  if(prt) std::cout<<"FMV: ipl "<<ipl<<" icl "<<icl<<" end "<<end
1466  <<" vtx wire "<<geom->WireCoordinate(vtx[ivx].Y, vtx[ivx].Z, ipl, tpc, cstat)
1467  <<" cls wire "<<cls[ipl][icl].Wire[end]
1468  <<" dWire "<<dWire<<"\n";
1469  */
1470  if(end == 0) {
1471  if(dWire > 30 || dWire < -2) continue;
1472  } else {
1473  if(dWire < -30 || dWire > 2) continue;
1474  }
1475  // project the cluster to the vertex wire
1476  dX = fabs(cls[ipl][icl].X[end] + cls[ipl][icl].Slope[end] * fWirePitch * dWire - vtx[ivx].X);
1477  // if(prt) std::cout<<"dX "<<dX<<"\n";
1478  if(dX < best) {
1479  best = dX;
1480  ibstvx = ivx;
1481  }
1482  } // ivx
1483  if(ibstvx >= 0) {
1484  // attach
1485  cls[ipl][icl].VtxIndex[end] = ibstvx;
1486  cls[ipl][icl].mVtxIndex[end] = ibstvx;
1487  }
1488  } // end
1489  } // icl
1490  } // ipl
1491 
1492  } // FindMaybeVertices
1493 
1495  void CCTrackMaker::MakeClusterChains(art::FindManyP<recob::Hit> const& fmCluHits)
1496  {
1497 
1498  unsigned short ipl, icl, icl1, icl2;
1499  float dw, dx, dWCut, dw1Max, dw2Max;
1500  float dA, dA2, dACut = fMaxDAng, chgAsymCut;
1501  float dXCut, chgasym, mrgErr;
1502  // long straight clusters
1503  bool ls1, ls2;
1504  bool gotprt = false;
1505  for(ipl = 0; ipl < nplanes; ++ipl) {
1506  if(cls[ipl].size() > 1) {
1507  for(icl1 = 0; icl1 < cls[ipl].size() - 1; ++icl1) {
1508  prt = (fDebugAlg == 666 && ipl == fDebugPlane && icl1 == fDebugCluster);
1509  if(prt) gotprt = true;
1510  // maximum delta Wire overlap is 10% of the total length
1511  dw1Max = 0.6 * cls[ipl][icl1].Length;
1512  ls1 = (cls[ipl][icl1].Length > 100 && fabs(cls[ipl][icl1].Angle[0] - cls[ipl][icl1].Angle[1]) < 0.04);
1513  for(icl2 = icl1 + 1; icl2 < cls[ipl].size(); ++icl2) {
1514  ls2 = (cls[ipl][icl2].Length > 100 && fabs(cls[ipl][icl2].Angle[0] - cls[ipl][icl2].Angle[1]) < 0.04);
1515  dw2Max = 0.6 * cls[ipl][icl2].Length;
1516  // set overlap cut to be the shorter of the two
1517  dWCut = dw1Max;
1518  if(dw2Max < dWCut) dWCut = dw2Max;
1519  // but not exceeding 20 for very long clusters
1520  if(dWCut > 100) dWCut = 100;
1521  if(dWCut < 2) dWCut = 2;
1522  chgAsymCut = fMergeChgAsym;
1523  // Compare end 1 of icl1 with end 0 of icl2
1524 
1525  if(prt) mf::LogVerbatim("CCTM")<<"MCC P:C:W icl1 "<<ipl<<":"<<icl1<<":"<<cls[ipl][icl1].Wire[1]<<" vtx "<<cls[ipl][icl1].VtxIndex[1]<<" ls1 "<<ls1<<" icl2 "<<ipl<<":"<<icl2<<":"<<cls[ipl][icl2].Wire[0]<<" vtx "<<cls[ipl][icl2].VtxIndex[0]<<" ls2 "<<ls2<<" dWCut "<<dWCut;
1526  if(std::abs(cls[ipl][icl1].Wire[1] - cls[ipl][icl2].Wire[0]) > dWCut) continue;
1527  // ignore if the clusters begin/end on the same wire
1528 // if(cls[ipl][icl1].Wire[1] == cls[ipl][icl2].Wire[0]) continue;
1529  // or if the angle exceeds the cut
1530  float af = AngleFactor(cls[ipl][icl1].Slope[1]);
1531  dACut = fMaxDAng * af;
1532  dXCut = fChainMaxdX * 5 * af;
1533  dA = fabs(cls[ipl][icl1].Angle[1] - cls[ipl][icl2].Angle[0]);
1534  // compare the match angle at the opposite ends of the clusters.
1535  // May have a bad end/begin angle if there is a delta-ray in the middle
1536  dA2 = fabs(cls[ipl][icl1].Angle[0] - cls[ipl][icl2].Angle[1]);
1537 
1538  if(prt) mf::LogVerbatim("CCTM")<<" dA "<<dA<<" dA2 "<<dA2<<" DACut "<<dACut<<" dXCut "<<dXCut;
1539 
1540  if(dA2 < dA) dA = dA2;
1541  // ignore vertices that have only two associated clusters and
1542  // the angle is small
1543  if(dA < fChainVtxAng && cls[ipl][icl1].VtxIndex[1] >= 0) {
1544  dw = fWirePitch * (cls[ipl][icl2].Wire[0] - cls[ipl][icl1].Wire[1]);
1545  dx = cls[ipl][icl1].X[1] + cls[ipl][icl1].Slope[1] * dw * fWirePitch - cls[ipl][icl2].X[0];
1546  unsigned short ivx = cls[ipl][icl1].VtxIndex[1];
1547  if(vtx[ivx].nClusInPln[ipl] == 2 && fabs(dx) < 1) {
1548  cls[ipl][icl1].VtxIndex[1] = -2;
1549  cls[ipl][icl2].VtxIndex[0] = -2;
1550  vtx[ivx].nClusInPln[ipl] = 0;
1551  if(prt) mf::LogVerbatim("CCTM")<<" clobbered vertex "<<ivx;
1552  } // vertices match
1553  } // dA < 0.1 && ...
1554 
1555  // don't merge if a vertex exists at these ends
1556  if(cls[ipl][icl1].VtxIndex[1] >= 0) continue;
1557  if(cls[ipl][icl2].VtxIndex[0] >= 0) continue;
1558 
1559  // expand the angle match cut for clusters that appear to be stopping
1560  if(cls[ipl][icl2].Wire[0] - cls[ipl][icl1].Wire[1] < 3 &&
1561  (cls[ipl][icl1].Length < 3 || cls[ipl][icl2].Length < 3) ) {
1562  if(prt) mf::LogVerbatim("CCTM")<<"Stopping cluster";
1563  dACut *= 1.5;
1564  chgAsymCut *= 1.5;
1565  dXCut *= 3;
1566  } // stopping US cluster
1567 
1568  // find the angle made by the endpoints of the clusters
1569  dw = fWirePitch * (cls[ipl][icl2].Wire[0] - cls[ipl][icl1].Wire[1]);
1570  if(dw != 0) {
1571  dx = cls[ipl][icl2].X[0] - cls[ipl][icl1].X[1];
1572  float dA3 = std::abs(atan(dx / dw) - cls[ipl][icl1].Angle[1]);
1573  if(prt) mf::LogVerbatim("CCTM")<<" dA3 "<<dA3;
1574  if(dA3 > dA) dA = dA3;
1575  }
1576 
1577  // angle matching
1578  if(dA > dACut) continue;
1579 
1580  if(prt) mf::LogVerbatim("CCTM")<<" rough dX "<<fabs(cls[ipl][icl1].X[1] - cls[ipl][icl2].X[0])<<" cut = 20";
1581 
1582  // make a rough dX cut
1583  if(fabs(cls[ipl][icl1].X[1] - cls[ipl][icl2].X[0]) > 20) continue;
1584 
1585  // handle cosmic ray clusters that are broken at delta rays
1586  if(ls1 || ls2) {
1587  // tighter angle cuts but no charge cut
1588  if(dA > fChainVtxAng) continue;
1589  } else {
1590  chgasym = fabs(cls[ipl][icl1].Charge[1] - cls[ipl][icl2].Charge[0]);
1591  chgasym /= cls[ipl][icl1].Charge[1] + cls[ipl][icl2].Charge[0];
1592  if(prt) mf::LogVerbatim("CCTM")<<" chgasym "<<chgasym<<" cut "<<chgAsymCut;
1593  if(chgasym > chgAsymCut) continue;
1594  } // ls1 || ls2
1595  // project the longer cluster to the end of the shorter one
1596  if(cls[ipl][icl1].Length > cls[ipl][icl2].Length) {
1597  dw = fWirePitch * (cls[ipl][icl2].Wire[0] - cls[ipl][icl1].Wire[1]);
1598  dx = cls[ipl][icl1].X[1] + cls[ipl][icl1].Slope[1] * dw * fWirePitch - cls[ipl][icl2].X[0];
1599  } else {
1600  dw = fWirePitch * (cls[ipl][icl1].Wire[1] - cls[ipl][icl2].Wire[0]);
1601  dx = cls[ipl][icl2].X[0] + cls[ipl][icl2].Slope[0] * dw * fWirePitch - cls[ipl][icl1].X[1];
1602  }
1603 
1604  // handle overlapping clusters
1605  if(dA2 < 0.01 && abs(dx) > dXCut && dx < -1) {
1606  dx = dXClTraj(fmCluHits, ipl, icl1, 1, icl2);
1607  if(prt) mf::LogVerbatim("CCTM")<<" new dx from dXClTraj "<<dx;
1608  }
1609 
1610  if(prt) mf::LogVerbatim("CCTM")<<" X0 "<<cls[ipl][icl1].X[1]<<" slp "<<cls[ipl][icl1].Slope[1]<<" dw "<<dw<<" oX "<<cls[ipl][icl2].X[0]<<" dx "<<dx<<" cut "<<dXCut;
1611 
1612  if(fabs(dx) > dXCut) continue;
1613 
1614  // calculate a merge error that will be used to adjudicate between multiple merge attempts AngleFactor
1615  float xerr = dx / dXCut;
1616  float aerr = dA / dACut;
1617  mrgErr = xerr * xerr + aerr * aerr;
1618 
1619  if(prt) mf::LogVerbatim("CCTM")<<"icl1 mrgErr "<<mrgErr<<" MergeError "<<cls[ipl][icl1].MergeError[1]<<" icl2 MergeError "<<cls[ipl][icl2].MergeError[0];
1620 
1621  // this merge better than a previous one?
1622  if(mrgErr > cls[ipl][icl1].MergeError[1]) continue;
1623  if(mrgErr > cls[ipl][icl2].MergeError[0]) continue;
1624 
1625  // un-merge icl1 - this should always be true but check anyway
1626  if(cls[ipl][icl1].BrkIndex[1] >= 0) {
1627  unsigned short ocl = cls[ipl][icl1].BrkIndex[1];
1628  if(prt) mf::LogVerbatim("CCTM")<<"clobber old icl1 BrkIndex "<<ocl;
1629  if(cls[ipl][ocl].BrkIndex[0] == icl1) {
1630  cls[ipl][ocl].BrkIndex[0] = -1;
1631  cls[ipl][ocl].MergeError[0] = fMaxMergeError;
1632  }
1633  if(cls[ipl][ocl].BrkIndex[1] == icl1) {
1634  cls[ipl][ocl].BrkIndex[1] = -1;
1635  cls[ipl][ocl].MergeError[1] = fMaxMergeError;
1636  }
1637  } // cls[ipl][icl1].BrkIndex[1] >= 0
1638  cls[ipl][icl1].BrkIndex[1] = icl2;
1639  cls[ipl][icl1].MergeError[1] = mrgErr;
1640 
1641  // un-merge icl2
1642  if(cls[ipl][icl2].BrkIndex[0] >= 0) {
1643  unsigned short ocl = cls[ipl][icl2].BrkIndex[0];
1644  if(prt) mf::LogVerbatim("CCTM")<<"clobber old icl2 BrkIndex "<<ocl;
1645  if(cls[ipl][ocl].BrkIndex[0] == icl1) {
1646  cls[ipl][ocl].BrkIndex[0] = -1;
1647  cls[ipl][ocl].MergeError[0] = fMaxMergeError;
1648  }
1649  if(cls[ipl][ocl].BrkIndex[1] == icl1) {
1650  cls[ipl][ocl].BrkIndex[1] = -1;
1651  cls[ipl][ocl].MergeError[1] = fMaxMergeError;
1652  }
1653  } // cls[ipl][icl2].BrkIndex[0] >= 0
1654  cls[ipl][icl2].BrkIndex[0] = icl1;
1655  cls[ipl][icl2].MergeError[0] = mrgErr;
1656  if(prt) mf::LogVerbatim("CCTM")<<" merge "<<icl1<<" and "<<icl2;
1657 
1658  } // icl2
1659  } // icl1
1660 
1661  // look for broken clusters in which have a C shape similar to a Begin-Begin vertex. The clusters
1662  // will have large and opposite sign angles
1663  bool gotone;
1664  for(icl1 = 0; icl1 < cls[ipl].size() - 1; ++icl1) {
1665  gotone = false;
1666  for(icl2 = icl1 + 1; icl2 < cls[ipl].size(); ++icl2) {
1667  // check both ends
1668  for(unsigned short end = 0; end < 2; ++end) {
1669  // Ignore already identified broken clusters
1670  if(cls[ipl][icl1].BrkIndex[end] >= 0) continue;
1671  if(cls[ipl][icl2].BrkIndex[end] >= 0) continue;
1672 // if(prt) mf::LogVerbatim("CCTM")<<"BrokenC: clusters "<<cls[ipl][icl1].Wire[end]<<":"<<(int)cls[ipl][icl1].Time[end]<<" "<<cls[ipl][icl2].Wire[end]<<":"<<(int)cls[ipl][icl2].Time[end]<<" angles "<<cls[ipl][icl1].Angle[end]<<" "<<cls[ipl][icl2].Angle[end];
1673  // require a large angle cluster
1674  if(fabs(cls[ipl][icl1].Angle[end]) < 1) continue;
1675  // and a second large angle cluster
1676  if(fabs(cls[ipl][icl2].Angle[end]) < 1) continue;
1677  if(prt) mf::LogVerbatim("CCTM")<<"BrokenC: clusters "<<cls[ipl][icl1].Wire[end]<<":"<<(int)cls[ipl][icl1].Time[end]<<" "<<cls[ipl][icl2].Wire[end]<<":"<<(int)cls[ipl][icl2].Time[end]<<" angles "<<cls[ipl][icl1].Angle[end]<<" "<<cls[ipl][icl2].Angle[end]<<" dWire "<<fabs(cls[ipl][icl1].Wire[end] - cls[ipl][icl2].Wire[end]);
1678  if(fabs(cls[ipl][icl1].Wire[end] - cls[ipl][icl2].Wire[end]) > 5) continue;
1679  // This is really crude but maybe OK
1680  // project 1 -> 2
1681  float dsl = cls[ipl][icl2].Slope[end] - cls[ipl][icl1].Slope[end];
1682  float fvw = (cls[ipl][icl1].X[end] - cls[ipl][icl1].Wire[end] * cls[ipl][icl1].Slope[end] - cls[ipl][icl2].X[end] + cls[ipl][icl2].Wire[end] * cls[ipl][icl2].Slope[end]) / dsl;
1683  if(prt) mf::LogVerbatim("CCTM")<<" fvw "<<fvw;
1684  if(fabs(cls[ipl][icl1].Wire[end] - fvw) > 4) continue;
1685  if(fabs(cls[ipl][icl2].Wire[end] - fvw) > 4) continue;
1686  cls[ipl][icl1].BrkIndex[end] = icl2;
1687  // TODO This could use some improvement if necessary
1688  cls[ipl][icl1].MergeError[end] = 1;
1689  cls[ipl][icl2].BrkIndex[end] = icl1;
1690  cls[ipl][icl2].MergeError[end] = 1;
1691  gotone = true;
1692  dx = fabs(cls[ipl][icl1].X[end] - cls[ipl][icl2].X[end]);
1693  if(prt) mf::LogVerbatim("CCTM")<<"BrokenC: icl1:W "<<icl1<<":"<<cls[ipl][icl1].Wire[end]<<" icl2:W "<<icl2<<":"<<cls[ipl][icl2].Wire[end]<<" end "<<end<<" dx "<<dx;
1694  } // end
1695  if(gotone) break;
1696  } // icl2
1697  } // icl1
1698 
1699  } // cls[ipl].size() > 1
1700 
1701  // follow mother-daughter broken clusters and put them in the cluster chain array
1702  unsigned short end, mom, momBrkEnd, dtrBrkEnd, nit;
1703  short dtr;
1704 
1705  std::vector<bool> gotcl(cls[ipl].size());
1706  for(icl = 0; icl < cls[ipl].size(); ++icl) gotcl[icl] = false;
1707  if(prt) mf::LogVerbatim("CCTM")<<"ipl "<<ipl<<" cls.size() "<<cls[ipl].size()<<"\n";
1708 
1709  std::vector<unsigned short> sCluster;
1710  std::vector<unsigned short> sOrder;
1711  for(icl = 0; icl < cls[ipl].size(); ++icl) {
1712  sCluster.clear();
1713  sOrder.clear();
1714  if(gotcl[icl]) continue;
1715  // don't start with a cluster broken at both ends
1716  if(cls[ipl][icl].BrkIndex[0] >= 0 && cls[ipl][icl].BrkIndex[1] >= 0) continue;
1717  for(end = 0; end < 2; ++end) {
1718  if(cls[ipl][icl].BrkIndex[end] < 0) continue;
1719  if(cls[ipl][icl].MergeError[end] > fMergeErrorCut) continue;
1720  gotcl[icl] = true;
1721  mom = icl;
1722  // end where the mom is broken
1723  momBrkEnd = end;
1724  sCluster.push_back(mom);
1725  if(momBrkEnd == 1) {
1726  // typical case - broken at the DS end
1727  sOrder.push_back(0);
1728  } else {
1729  // broken at the US end
1730  sOrder.push_back(1);
1731  }
1732  dtr = cls[ipl][icl].BrkIndex[end];
1733 // std::cout<<"Starting mom:momBrkEnd "<<mom<<":"<<momBrkEnd<<" dtr "<<dtr<<"\n";
1734  nit = 0;
1735  while(dtr >= 0 && dtr < (short)cls[ipl].size() && nit < cls[ipl].size()) {
1736  // determine which end of the dtr should be attached to mom
1737  for(dtrBrkEnd = 0; dtrBrkEnd < 2; ++dtrBrkEnd) if(cls[ipl][dtr].BrkIndex[dtrBrkEnd] == mom) break;
1738  if(dtrBrkEnd == 2) {
1739 // mf::LogError("CCTM")<<"Cant find dtrBrkEnd for cluster "<<icl<<" dtr "<<dtr<<" in plane "<<ipl;
1740  gotcl[icl] = false;
1741  break;
1742  }
1743  // check for reasonable merge error
1744  if(cls[ipl][dtr].MergeError[dtrBrkEnd] < fMergeErrorCut) {
1745  sCluster.push_back(dtr);
1746  sOrder.push_back(dtrBrkEnd);
1747  gotcl[dtr] = true;
1748  }
1749 // std::cout<<" dtr:dtrBrkEnd "<<dtr<<":"<<dtrBrkEnd<<" momBrkEnd "<<momBrkEnd<<"\n";
1750  ++nit;
1751  // set up to check the new mom
1752  mom = dtr;
1753  // at the other end
1754  momBrkEnd = 1 - dtrBrkEnd;
1755  // with the new dtr
1756  dtr = cls[ipl][mom].BrkIndex[momBrkEnd];
1757 // std::cout<<" new mom:momBrkEnd "<<mom<<":"<<momBrkEnd<<" new dtr "<<dtr<<"\n";
1758  } // dtr >= 0 ...
1759  if(dtrBrkEnd == 2) continue;
1760  } // end
1761 
1762  if(!gotcl[icl]) {
1763  // a single unbroken cluster
1764  sCluster.push_back(icl);
1765  sOrder.push_back(0);
1766  }
1767 
1768  if(sCluster.size() == 0) {
1769  mf::LogError("CCTM")<<"MakeClusterChains error in plane "<<ipl<<" cluster "<<icl;
1770  return;
1771  }
1772 /*
1773  std::cout<<ipl<<" icl "<<icl<<" sCluster ";
1774  for(unsigned short ii = 0; ii < sCluster.size(); ++ii) std::cout<<" "<<sCluster[ii]<<":"<<sOrder[ii];
1775  std::cout<<"\n";
1776 */
1777  ClsChainPar ccp;
1778  // fill the struct parameters assuming this is a cluster chain containing only one cluster
1779  unsigned short jcl = sCluster[0];
1780  if(jcl > cls[ipl].size()) std::cout<<"oops MCC\n";
1781  unsigned short oend;
1782  for(end = 0; end < 2; ++end) {
1783  oend = end;
1784  if(sOrder[0] > 0) oend = 1 - end;
1785  ccp.Wire[end] = cls[ipl][jcl].Wire[oend];
1786  ccp.Time[end] = cls[ipl][jcl].Time[oend];
1787  ccp.X[end] = cls[ipl][jcl].X[oend];
1788  ccp.Slope[end] = cls[ipl][jcl].Slope[oend];
1789  ccp.Angle[end] = cls[ipl][jcl].Angle[oend];
1790  ccp.Dir[end] = cls[ipl][icl].Dir[oend];
1791  ccp.VtxIndex[end] = cls[ipl][jcl].VtxIndex[oend];
1792  ccp.ChgNear[end] = cls[ipl][jcl].ChgNear[oend];
1793  ccp.mBrkIndex[end] = cls[ipl][jcl].BrkIndex[oend];
1794  } // end
1795  ccp.Length = cls[ipl][icl].Length;
1796  ccp.TotChg = cls[ipl][icl].TotChg;
1797  ccp.InTrack = -1;
1798 // std::cout<<"sOrder0 "<<sOrder[0]<<" "<<(int)ccp.Wire[0]<<":"<<(int)ccp.Time[0]<<" dir "<<ccp.Dir[0]<<" "<<(int)ccp.Wire[1]<<":"<<(int)ccp.Time[1]<<" dir "<<ccp.Dir[1]<<"\n";
1799 
1800  for(unsigned short ii = 1; ii < sCluster.size(); ++ii) {
1801  jcl = sCluster[ii];
1802  if(jcl > cls[ipl].size()) std::cout<<"oops MCC\n";
1803  // end is the end where the break is being mended
1804  end = sOrder[ii];
1805  if(end > 1) std::cout<<"oops2 MCC\n";
1806  oend = 1 - end;
1807  // update the parameters at the other end of the chain
1808  ccp.Wire[1] = cls[ipl][jcl].Wire[oend];
1809  ccp.Time[1] = cls[ipl][jcl].Time[oend];
1810  ccp.X[1] = cls[ipl][jcl].X[oend];
1811  ccp.Slope[1] = cls[ipl][jcl].Slope[oend];
1812  ccp.Angle[1] = cls[ipl][jcl].Angle[oend];
1813  ccp.Dir[1] = cls[ipl][jcl].Dir[oend];
1814  ccp.VtxIndex[1] = cls[ipl][jcl].VtxIndex[oend];
1815  ccp.ChgNear[1] = cls[ipl][jcl].ChgNear[oend];
1816  ccp.mBrkIndex[1] = cls[ipl][jcl].BrkIndex[oend];
1817  ccp.Length += cls[ipl][jcl].Length;
1818  ccp.TotChg += cls[ipl][jcl].TotChg;
1819 // std::cout<<"Update "<<end<<" "<<(int)ccp.Wire[0]<<":"<<(int)ccp.Time[0]<<" dir "<<ccp.Dir[0]<<" "<<(int)ccp.Wire[1]<<":"<<(int)ccp.Time[1]<<" dir "<<ccp.Dir[1]<<"\n";
1820  } // ii
1821  ccp.ClsIndex = sCluster;
1822  ccp.Order = sOrder;
1823  // redo the direction
1824  if(ccp.Time[1] > ccp.Time[0]) {
1825  ccp.Dir[0] = 1; ccp.Dir[1] = -1;
1826  } else {
1827  ccp.Dir[0] = -1; ccp.Dir[1] = 1;
1828  }
1829  clsChain[ipl].push_back(ccp);
1830 
1831  } // icl
1832 
1833  // re-index mBrkIndex to point to a cluster chain, not a cluster
1834  unsigned short brkCls;
1835  bool gotit;
1836  for(unsigned short ccl = 0; ccl < clsChain[ipl].size(); ++ccl) {
1837  for(unsigned short end = 0; end < 2; ++end) {
1838  if(clsChain[ipl][ccl].mBrkIndex[end] < 0) continue;
1839  brkCls = clsChain[ipl][ccl].mBrkIndex[end];
1840  gotit = false;
1841  // find this cluster index in a cluster chain
1842  for(unsigned short ccl2 = 0; ccl2 < clsChain[ipl].size(); ++ccl2) {
1843  if(ccl2 == ccl) continue;
1844  if(std::find(clsChain[ipl][ccl2].ClsIndex.begin(), clsChain[ipl][ccl2].ClsIndex.end(), brkCls) == clsChain[ipl][ccl2].ClsIndex.end()) continue;
1845  // found it
1846  clsChain[ipl][ccl].mBrkIndex[end] = ccl2;
1847  gotit = true;
1848  break;
1849  } // ccl2
1850 // if(gotit) std::cout<<"gotit ccl "<<ccl<<" end "<<end<<" new mBrkIndex "<<clsChain[ipl][ccl].mBrkIndex[end]<<"\n";
1851  if(!gotit) mf::LogError("CCTM")<<"MCC: Cluster chain "<<ccl<<" end "<<end<<" Failed to find brkCls "<<brkCls<<" in plane "<<ipl;
1852  } // end
1853  } // ccl
1854 
1855  } // ipl
1856 
1857 
1858  if(gotprt) PrintClusters();
1859  prt = false;
1860 
1861  } // MakeClusterChains
1862 
1864  float CCTrackMaker::dXClTraj(art::FindManyP<recob::Hit> const& fmCluHits, unsigned short ipl, unsigned short icl1, unsigned short end1, unsigned short icl2)
1865  {
1866  // project cluster icl1 at end1 to find the best intersection with icl2
1867  float dw, dx, best = 999;
1868  std::vector<art::Ptr<recob::Hit>> clusterhits = fmCluHits.at(cls[ipl][icl1].EvtIndex);
1869  for(unsigned short hit = 0; hit < clusterhits.size(); ++hit) {
1870  dw = clusterhits[hit]->WireID().Wire - cls[ipl][icl1].Wire[end1];
1871  dx = fabs(cls[ipl][icl1].Time[end1] + dw * fWirePitch * cls[ipl][icl1].Slope[end1] - clusterhits[hit]->PeakTime());
1872  if(dx < best) best = dx;
1873  if(dx < 0.01) break;
1874  } // hit
1875  return best;
1876  } // dXClTraj
1877 
1879  void CCTrackMaker::StoreTrack(art::FindManyP<recob::Hit> const& fmCluHits,
1880  unsigned short imat, unsigned short procCode)
1881  {
1882  // store the current "under construction" track in the trk vector
1883 
1884  TrkPar newtrk;
1885 
1886  if(imat > matcomb.size() - 1) {
1887  mf::LogError("CCTM")<<"Bad imat in StoreTrack";
1888  return;
1889  }
1890 
1891  // ensure there are at least 2 hits in at least 2 planes
1892  unsigned short nhitinpl = 0;
1893  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) if(trkHits[ipl].size() > 1) ++nhitinpl;
1894  if(nhitinpl < 2) {
1895  mf::LogError("CCTM")<<"StoreTrack: Not enough hits in each plane\n";
1896  return;
1897  }
1898  if(prt) mf::LogVerbatim("CCTM")<<"In StoreTrack: matcomb "<<imat<<" cluster chains "<<matcomb[imat].Cls[0]<<" "<<matcomb[imat].Cls[1]<<" "<<matcomb[imat].Cls[2];
1899 
1900  // Track hit vectors for fitting the trajectory
1901  std::array<std::vector<geo::WireID>,3> trkWID;
1902  std::array<std::vector<double>,3> trkX;
1903  std::array<std::vector<double>,3> trkXErr;
1904 
1905  // track trajectory for a track
1906  std::vector<TVector3> trkPos;
1907  std::vector<TVector3> trkDir;
1908 
1909  newtrk.ID = trk.size() + 1;
1910  newtrk.Proc = procCode;
1911  newtrk.TrkHits = trkHits;
1912  // BUG the double brace syntax is required to work around clang bug 21629
1913  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
1914  newtrk.VtxIndex = {{-1, -1}};
1915  newtrk.ChgOrder = 0;
1916  newtrk.MomID = -1;
1917  // BUG the double brace syntax is required to work around clang bug 21629
1918  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
1919  newtrk.EndInTPC = {{false, false}};
1920  newtrk.GoodEnd = {{false, false}};
1921  newtrk.DtrID = {0};
1922  newtrk.PDGCode = -1;
1923 
1924  unsigned short ipl, icl, iht;
1925 
1926  if(prt) mf::LogVerbatim("CCTM")<<"CCTM: Make traj for track "<<newtrk.ID<<" procCode "<<procCode<<" nhits in planes "<<trkHits[0].size()<<" "<<trkHits[1].size()<<" "<<trkHits[2].size();
1927  // make the track trajectory
1928  if(nplanes == 2) {
1929  trkWID[2].resize(0);
1930  trkX[2].resize(0);
1931  trkXErr[2].resize(0);
1932  }
1933  for(ipl = 0; ipl < nplanes; ++ipl) {
1934  trkWID[ipl].resize(trkHits[ipl].size());
1935  trkX[ipl].resize(trkHits[ipl].size());
1936  trkXErr[ipl].resize(trkHits[ipl].size());
1937  for(iht = 0; iht < trkHits[ipl].size(); ++iht) {
1938  trkWID[ipl][iht] = trkHits[ipl][iht]->WireID();
1939  trkX[ipl][iht] = detprop->ConvertTicksToX(trkHits[ipl][iht]->PeakTime(),ipl, tpc, cstat);
1940  trkXErr[ipl][iht] = fHitFitErrFac * trkHits[ipl][iht]->RMS() * trkHits[ipl][iht]->Multiplicity();
1941 // std::cout<<iht<<" "<<trkWID[ipl][iht]<<" "<<trkX[ipl][iht]<<" "<<trkXErr[ipl][iht]<<"\n";
1942  } // iht
1943  } // ipl
1944  fTrackTrajectoryAlg.TrackTrajectory(trkWID, trkX, trkXErr, trkPos, trkDir);
1945  if(trkPos.size() < 2) {
1946  mf::LogError("CCTM")<<"StoreTrack: No trajectory points on failed track "<<newtrk.ID
1947  <<" in StoreTrack: matcomb "<<imat<<" cluster chains "<<matcomb[imat].Cls[0]<<" "<<matcomb[imat].Cls[1]<<" "<<matcomb[imat].Cls[2];
1948  // make a garbage trajectory
1949  trkPos.resize(2);
1950  trkPos[1](2) = 1;
1951  trkDir.resize(2);
1952  trkDir[1](2) = 1;
1953  }
1954  newtrk.TrjPos = trkPos;
1955  newtrk.TrjDir = trkDir;
1956 
1957  if(prt) mf::LogVerbatim("CCTM")<<" number of traj points "<<trkPos.size();
1958 
1959  // determine if each end is good in the sense that there are hits in each plane
1960  // that are consistent in time and are presumed to form a good 3D space point
1961  unsigned short end, nClose, indx, jndx;
1962  float xErr;
1963  for(end = 0; end < 2; ++end) {
1964  nClose = 0;
1965  for(ipl = 0; ipl < nplanes - 1; ++ipl) {
1966  if(trkX[ipl].size() == 0) continue;
1967  for(unsigned short jpl = ipl + 1; jpl < nplanes; ++jpl) {
1968  if(trkX[jpl].size() == 0) continue;
1969  if(end == 0) {
1970  indx = 0;
1971  jndx = 0;
1972  } else {
1973  indx = trkXErr[ipl].size() - 1;
1974  jndx = trkXErr[jpl].size() - 1;
1975  }
1976  xErr = 3 * (trkXErr[ipl][indx] + trkXErr[jpl][jndx]);
1977  if(std::abs(trkX[ipl][indx] - trkX[jpl][jndx]) < xErr) ++nClose;
1978  } // jpl
1979  } // ipl
1980  if(nClose == nplanes) newtrk.GoodEnd[end] = true;
1981  } // end
1982 
1983  // set trajectory end points to a vertex if one exists
1984  unsigned short ivx, itj, ccl;
1985  float dx, dy, dz, dr0, dr1;
1986  unsigned short attachEnd;
1987  for(end = 0; end < 2; ++end) {
1988  ivx = USHRT_MAX;
1989  if(end == 0 && matcomb[imat].Vtx >= 0) ivx = matcomb[imat].Vtx;
1990  if(end == 1 && matcomb[imat].oVtx >= 0) ivx = matcomb[imat].oVtx;
1991  if(ivx == USHRT_MAX) continue;
1992  // determine the proper end using the TrjPos order and brute force
1993  itj = 0;
1994  dx = vtx[ivx].X - newtrk.TrjPos[itj](0);
1995  dy = vtx[ivx].Y - newtrk.TrjPos[itj](1);
1996  dz = vtx[ivx].Z - newtrk.TrjPos[itj](2);
1997  dr0 = dx*dx + dy*dy + dz*dz;
1998  itj = newtrk.TrjPos.size() - 1;
1999  dx = vtx[ivx].X - newtrk.TrjPos[itj](0);
2000  dy = vtx[ivx].Y - newtrk.TrjPos[itj](1);
2001  dz = vtx[ivx].Z - newtrk.TrjPos[itj](2);
2002  dr1 = dx*dx + dy*dy + dz*dz;
2003  attachEnd = 1;
2004  if(dr0 < dr1) {
2005  itj = 0;
2006  attachEnd = 0;
2007  // a really bad match to the vertex
2008  if(dr0 > 5) return;
2009  } else {
2010  // a really bad match to the vertex
2011  if(dr1 > 5) return;
2012  }
2013  newtrk.TrjPos[itj](0) = vtx[ivx].X;
2014  newtrk.TrjPos[itj](1) = vtx[ivx].Y;
2015  newtrk.TrjPos[itj](2) = vtx[ivx].Z;
2016  newtrk.VtxIndex[attachEnd] = ivx;
2017  // correct the trajectory direction
2018  TVector3 dir;
2019  if(itj == 0) {
2020  dir = newtrk.TrjPos[1] - newtrk.TrjPos[0];
2021  newtrk.TrjDir[0] = dir.Unit();
2022  } else {
2023  dir = newtrk.TrjPos[itj - 1] - newtrk.TrjPos[itj];
2024  newtrk.TrjDir[itj] = dir.Unit();
2025  }
2026  } // end
2027 
2028  if(newtrk.VtxIndex[0] >= 0 && newtrk.VtxIndex[0] == newtrk.VtxIndex[1]) {
2029  mf::LogError("CCTM")<<"StoreTrack: Trying to attach a vertex to both ends of a track. imat = "<<imat;
2030  return;
2031  }
2032 
2033  // calculate the length
2034  newtrk.Length = 0;
2035  float norm;
2036  double X, Y, Z;
2037  for(unsigned short itj = 1; itj < newtrk.TrjPos.size(); ++itj) {
2038  X = newtrk.TrjPos[itj](0) - newtrk.TrjPos[itj-1](0);
2039  Y = newtrk.TrjPos[itj](1) - newtrk.TrjPos[itj-1](1);
2040  Z = newtrk.TrjPos[itj](2) - newtrk.TrjPos[itj-1](2);
2041  norm = sqrt(X*X + Y*Y + Z*Z);
2042  newtrk.Length += norm;
2043  }
2044 
2045  // store the cluster -> track assignment
2046  newtrk.ClsEvtIndices.clear();
2047  for(ipl = 0; ipl < nplanes; ++ipl) {
2048  if(matcomb[imat].Cls[ipl] < 0) continue;
2049  ccl = matcomb[imat].Cls[ipl];
2050  if(ccl > clsChain[ipl].size()) std::cout<<"oops StoreTrack\n";
2051  clsChain[ipl][ccl].InTrack = newtrk.ID;
2052  for(unsigned short icc = 0; icc < clsChain[ipl][ccl].ClsIndex.size(); ++icc) {
2053  icl = clsChain[ipl][ccl].ClsIndex[icc];
2054  if(icl > cls[ipl].size()) std::cout<<"oops StoreTrack\n";
2055  cls[ipl][icl].InTrack = newtrk.ID;
2056  if(cls[ipl][icl].EvtIndex > fmCluHits.size() - 1) {
2057  std::cout<<"ooops2 store track EvtIndex "<<cls[ipl][icl].EvtIndex<<" size "<<fmCluHits.size()<<" icl "<<icl<<"\n";
2058  continue;
2059  }
2060  newtrk.ClsEvtIndices.push_back(cls[ipl][icl].EvtIndex);
2061  } // icc
2062  } // ipl
2063 
2064  if(prt) mf::LogVerbatim("CCTM")<<" track ID "<<newtrk.ID<<" stored in StoreTrack";
2065 
2066  trk.push_back(newtrk);
2067  } // StoreTrack
2068 /*
2070  void CCTrackMaker::AngMatch(art::FindManyP<recob::Hit> const& fmCluHits)
2071  {
2072  // look for long unused cluster chains and match them using angle
2073  std::array<std::vector<unsigned short>, 3> ccUnused;
2074 
2075  unsigned short ipl, icc, jpl, kpl, ii, jj, kk, icl, jcl, kcl, iend, jend, kend;
2076  short idir, jdir, kdir;
2077  float islp = 0, jslp = 0, kslp = 0, kAng = 0, sigmaA = 0, matchErr = 0;
2078 
2079  prt = (fDebugPlane >= 0);
2080 
2081  for(ipl = 0; ipl < nplanes; ++ipl) {
2082  for(icc = 0; icc < clsChain[ipl].size(); ++icc) {
2083  if(clsChain[ipl][icc].InTrack < 0 && clsChain[ipl][icc].Length > fAngMatchMinLen) ccUnused[ipl].push_back(icc);
2084  } // icc
2085  if(prt) {
2086  mf::LogVerbatim myprt("CCTM");
2087  myprt<<"AngMatch: ipl "<<ipl<<" ccUnused";
2088  for(icc = 0; icc < ccUnused[ipl].size(); ++icc) myprt<<" "<<ccUnused[ipl][icc];;
2089  }
2090  } // ipl
2091 
2092 
2093  if(ccUnused[0].size() == 0 && ccUnused[1].size() == 0 && ccUnused[2].size() == 0) return;
2094 
2095  std::array<float, 3> mchg;
2096  matcomb.clear();
2097 
2098  float xMatchWght = 1;
2099  if(fuBCode) xMatchWght = 0.1;
2100 
2101  for(ipl = 0; ipl < nplanes; ++ipl) {
2102  jpl = (ipl + 1) % nplanes;
2103  kpl = (jpl + 1) % nplanes;
2104  for(ii = 0; ii < ccUnused[ipl].size(); ++ii) {
2105  icl = ccUnused[ipl][ii];
2106  prt = (ipl == fDebugPlane && icl == fDebugCluster);
2107  for(jj = 0; jj < ccUnused[jpl].size(); ++jj) {
2108  jcl = ccUnused[jpl][jj];
2109  // make first charge asymmetry cut
2110  mchg[0] = clsChain[ipl][icl].TotChg;
2111  mchg[1] = clsChain[jpl][jcl].TotChg;
2112  mchg[2] = mchg[1];
2113  if(prt) mf::LogVerbatim("CCTM")<<"AngMatch: ipl:icl "<<ipl<<":"<<icl<<" jpl:jcl "<<jpl<<":"<<jcl<<" chg asym"<<ChargeAsym(mchg);
2114  if(ChargeAsym(mchg) > 0.5) continue;
2115  kslp = geom->ThirdPlaneSlope(ipl, islp, jpl, jslp, tpc, cstat);
2116  for(kk = 0; kk < ccUnused[kpl].size(); ++kk) {
2117  kcl = ccUnused[kpl][kk];
2118  // make second charge asymmetry cut
2119  mchg[0] = clsChain[ipl][icl].TotChg;
2120  mchg[1] = clsChain[jpl][jcl].TotChg;
2121  mchg[2] = clsChain[kpl][kcl].TotChg;
2122  if(!fuBCode && ChargeAsym(mchg) > 0.5) continue;
2123  // check the ends
2124  for(iend = 0; iend < 2; ++iend) {
2125  idir = clsChain[ipl][icl].Dir[iend];
2126  islp = clsChain[ipl][icl].Slope[iend];
2127  for(jend = 0; jend < 2; ++jend) {
2128  jdir = clsChain[jpl][jcl].Dir[jend];
2129  if(idir != 0 && jdir != 0 && idir != jdir) continue;
2130  jslp = clsChain[jpl][jcl].Slope[jend];
2131  // expected angle in kpl
2132  kslp = geom->ThirdPlaneSlope(ipl, islp, jpl, jslp, tpc, cstat);
2133  kAng = atan(kslp);
2134  sigmaA = fAngleMatchErr * AngleFactor(kslp);
2135  for(kend = 0; kend < 2; ++kend) {
2136  kdir = clsChain[kpl][kcl].Dir[kend];
2137  if(idir != 0 && kdir != 0 && idir != kdir) continue;
2138  matchErr = fabs(clsChain[kpl][kcl].Angle[kend] - kAng) / sigmaA;
2139  if(prt) mf::LogVerbatim("CCTM")<<" ipl:icl:iend "<<ipl<<":"<<icl<<":"<<iend<<" jpl:jcl:jend "<<jpl<<":"<<jcl<<":"<<jend<<" kpl:kcl:kend "<<kpl<<":"<<kcl<<":"<<kend<<" matchErr "<<matchErr;
2140  if(matchErr > 5) continue;
2141  MatchPars match;
2142  match.Cls[ipl] = icl; match.End[ipl] = iend;
2143  match.Cls[jpl] = jcl; match.End[jpl] = jend;
2144  match.Cls[kpl] = kcl; match.End[kpl] = kend;
2145  if(DupMatch(match)) continue;
2146  match.Chg[ipl] = clsChain[ipl][icl].TotChg;
2147  match.Chg[jpl] = clsChain[jpl][jcl].TotChg;
2148  match.Chg[kpl] = clsChain[kpl][kcl].TotChg;
2149  match.Vtx = -1;
2150  match.dWir = fabs(0.5 * (clsChain[ipl][icl].Wire[iend] + clsChain[jpl][jcl].Wire[jend]) - clsChain[kpl][kcl].Wire[kend]);
2151  match.dAng = fabs(clsChain[kpl][kcl].Angle[kend] - kAng);
2152  match.dX = xMatchWght * fabs(0.5 * (clsChain[ipl][icl].X[iend] + clsChain[jpl][jcl].X[jend]) - clsChain[kpl][kcl].X[kend]);
2153  if(prt) mf::LogVerbatim("CCTM")<<" match.dX "<<match.dX;
2154  if(!fuBCode && match.dX > 20) continue;
2155  if(std::abs(kAng) > 0.3 && match.dAng < 0.03) std::cout<<"match "<<ipl<<":"<<icl<<" "<<jpl<<":"<<jcl<<" "<<kpl<<":"<<kcl<<" "<<match.dAng<<"\n";
2156  // add X match error with 1 cm rms
2157 // match.Err = matchErr + match.dX;
2158  match.Err = 0.;
2159  match.oVtx = -1;
2160  match.odWir = 0;
2161  match.odAng = 0;
2162  match.odX = 0;
2163  match.oErr = 0;
2164  matcomb.push_back(match);
2165  } // kend
2166  } // jend
2167  } // iend
2168  } // kk
2169  } // jj
2170  } // ii
2171  } // ipl
2172 
2173  if(matcomb.size() == 0) return;
2174 
2175  SortMatches(fmCluHits, 3);
2176 
2177  prt = false;
2178  } // AngMatch
2179 */
2181  void CCTrackMaker::PlnMatch(art::FindManyP<recob::Hit> const& fmCluHits)
2182  {
2183  // Match clusters in all planes
2184  // unsigned short ipl, icl, jpl, jcl, kpl, kcl;
2185  bool ignoreSign;
2186  float kSlp, kAng, kX, kWir, okWir;
2187  short idir, ioend, jdir, joend, kdir;
2188 
2189  double yp, zp;
2190  float tpcSizeY = geom->DetHalfWidth();
2191  float tpcSizeZ = geom->DetLength();
2192 
2193 
2194  float dxcut = 2;
2195  float dxkcut;
2196  float dwcut = 6;
2197  if(fuBCode) {
2198  dxcut = 20;
2199  dwcut = 60;
2200  }
2201 
2202  // temp array for making a rough charge asymmetry cut
2203  std::array<float, 3> mchg;
2204 
2205  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
2206  for(unsigned short icl = 0; icl < clsChain[ipl].size(); ++icl) {
2207  if(clsChain[ipl][icl].InTrack >= 0) continue;
2208  // skip short clusters
2209  if(clsChain[ipl][icl].Length < fMatchMinLen[algIndex]) continue;
2210  unsigned short jpl = (ipl + 1) % nplanes;
2211  unsigned short kpl = (jpl + 1) % nplanes;
2212  for(unsigned short jcl = 0; jcl < clsChain[jpl].size(); ++jcl) {
2213  if(clsChain[jpl][jcl].InTrack >= 0) continue;
2214  // skip short clusters
2215  if(clsChain[jpl][jcl].Length < fMatchMinLen[algIndex]) continue;
2216  // make first charge asymmetry cut
2217  mchg[0] = clsChain[ipl][icl].TotChg;
2218  mchg[1] = clsChain[jpl][jcl].TotChg;
2219  mchg[2] = mchg[1];
2220  if(fChgAsymFactor[algIndex] > 0 && ChargeAsym(mchg) > 0.5) continue;
2221  for(unsigned short iend = 0; iend < 2; ++iend) {
2222  idir = clsChain[ipl][icl].Dir[iend];
2223  for(unsigned short jend = 0; jend < 2; ++jend) {
2224  jdir = clsChain[jpl][jcl].Dir[jend];
2225  if(idir != 0 && jdir != 0 && idir != jdir) continue;
2226  // make an X cut
2227  if(fabs(clsChain[ipl][icl].X[iend] - clsChain[jpl][jcl].X[jend]) > dxcut) continue;
2228  ioend = 1 - iend; joend = 1 - jend;
2229  // Find the expected third (k) plane parameters
2230  kSlp = geom->ThirdPlaneSlope(ipl, clsChain[ipl][icl].Slope[iend], jpl, clsChain[jpl][jcl].Slope[jend], tpc, cstat);
2231  kAng = atan(kSlp);
2232  // Ensure the match end is within the TPC
2233  geom->IntersectionPoint((unsigned int)(0.5+clsChain[ipl][icl].Wire[iend]),
2234  (unsigned int)(0.5+clsChain[jpl][jcl].Wire[jend]),
2235  ipl, jpl, cstat, tpc, yp, zp);
2236  if(yp > tpcSizeY || yp < -tpcSizeY) continue;
2237  if(zp < 0 || zp > tpcSizeZ) continue;
2238  kX = 0.5 * (clsChain[ipl][icl].X[iend] + clsChain[jpl][jcl].X[jend]);
2239  kWir = geom->WireCoordinate(yp, zp, kpl, tpc, cstat);
2240  // now look at the other end
2241  geom->IntersectionPoint((unsigned int)(0.5+clsChain[ipl][icl].Wire[ioend]),
2242  (unsigned int)(0.5+clsChain[jpl][jcl].Wire[joend]),
2243  ipl, jpl, cstat, tpc, yp, zp);
2244  if(yp > tpcSizeY || yp < -tpcSizeY) continue;
2245  if(zp < 0 || zp > tpcSizeZ) continue;
2246  okWir = geom->WireCoordinate(yp, zp, kpl, tpc, cstat);
2247  if(prt) mf::LogVerbatim("CCTM")<<"PlnMatch: chk i "<<ipl<<":"<<icl<<":"<<iend
2248  <<" idir "<<idir<<" X "<<clsChain[ipl][icl].X[iend]<<" j "<<jpl<<":"<<jcl<<":"<<jend
2249  <<" jdir "<<jdir<<" X "<<clsChain[jpl][jcl].X[jend];
2250 
2251  if(prt) mf::LogVerbatim("CCTM")<<"PlnMatch: chk j "<<ipl<<":"<<icl<<":"<<iend
2252  <<" "<<jpl<<":"<<jcl<<":"<<jend<<" iSlp "<<std::setprecision(2)<<clsChain[ipl][icl].Slope[iend]
2253  <<" jSlp "<<std::setprecision(2)<<clsChain[jpl][jcl].Slope[jend]<<" kWir "<<(int)kWir
2254  <<" okWir "<<(int)okWir<<" kSlp "<<std::setprecision(2)<<kSlp<<" kAng "
2255  <<std::setprecision(2)<<kAng<<" kX "<<std::setprecision(1)<<kX;
2256 
2257  // handle the case near pi/2, where the errors on large slopes
2258  // could result in a wrong-sign kAng
2259  ignoreSign = (fabs(kSlp) > 1.5);
2260  if(ignoreSign) kAng = fabs(kAng);
2261  dxkcut = dxcut * AngleFactor(kSlp);
2262  bool gotkcl = false;
2263  for(unsigned short kcl = 0; kcl < clsChain[kpl].size(); ++kcl) {
2264  if(clsChain[kpl][kcl].InTrack >= 0) continue;
2265  // make second charge asymmetry cut
2266  mchg[0] = clsChain[ipl][icl].TotChg;
2267  mchg[1] = clsChain[jpl][jcl].TotChg;
2268  mchg[2] = clsChain[kpl][kcl].TotChg;
2269  if(fChgAsymFactor[algIndex] > 0 && ChargeAsym(mchg) > 0.5) continue;
2270  for(unsigned short kend = 0; kend < 2; ++kend) {
2271  kdir = clsChain[kpl][kcl].Dir[kend];
2272  if(idir != 0 && kdir != 0 && idir != kdir) continue;
2273  if(prt) mf::LogVerbatim("CCTM")<<" kcl "<<kcl<<" kend "<<kend
2274  <<" dx "<<std::abs(clsChain[kpl][kcl].X[kend] - kX)<<" dxkcut "<<dxkcut;
2275  if(std::abs(clsChain[kpl][kcl].X[kend] - kX) > dxkcut) continue;
2276  // rough dWire cut
2277  if(prt) mf::LogVerbatim("CCTM")<<" kcl "<<kcl<<" kend "<<kend
2278  <<" dw "<<(clsChain[kpl][kcl].Wire[kend] - kWir)<<" ignoreSign "<<ignoreSign;
2279  if(fabs(clsChain[kpl][kcl].Wire[kend] - kWir) > dwcut) continue;
2280  if(prt) mf::LogVerbatim("CCTM")<<" chk k "<<kpl<<":"<<kcl<<":"<<kend;
2281  MatchPars match;
2282  match.Cls[ipl] = icl; match.End[ipl] = iend;
2283  match.Cls[jpl] = jcl; match.End[jpl] = jend;
2284  match.Cls[kpl] = kcl; match.End[kpl] = kend;
2285  match.Err = 100;
2286  if(DupMatch(match)) continue;
2287  match.Chg[ipl] = 0; match.Chg[jpl] = 0; match.Chg[kpl] = 0;
2288  match.Vtx = clsChain[ipl][icl].VtxIndex[iend];
2289  match.oVtx = -1;
2290  FillEndMatch(match);
2291  if(prt) mf::LogVerbatim("CCTM")<<" PlnMatch: match k "<<kpl<<":"<<match.Cls[kpl]
2292  <<":"<<match.End[kpl]<<" oChg "<<match.Chg[kpl]<<" mErr "<<match.Err<<" oErr "<<match.oErr;
2293  if(match.Chg[kpl] == 0) continue;
2294  if(match.Err > 10 || match.oErr > 10) continue;
2295  if(prt) mf::LogVerbatim("CCTM")<<" dup? ";
2296  if(DupMatch(match)) continue;
2297  matcomb.push_back(match);
2298  gotkcl = true;
2299  } // kend
2300  } // kcl
2301  if(prt) mf::LogVerbatim("CCTM")<<" PlnMatch: gotkcl "<<gotkcl;
2302  if(!gotkcl) {
2303  // make a 2-plane match and try again
2304  MatchPars match;
2305  match.Cls[ipl] = icl; match.End[ipl] = iend;
2306  match.Cls[jpl] = jcl; match.End[jpl] = jend;
2307  match.Cls[kpl] = -1; match.End[kpl] = 0;
2308  match.Err = 100;
2309  if(DupMatch(match)) continue;
2310  match.Chg[ipl] = 0; match.Chg[jpl] = 0; match.Chg[kpl] = 0;
2311  match.Vtx = clsChain[ipl][icl].VtxIndex[iend];
2312  match.oVtx = -1;
2313  FillEndMatch(match);
2314  if(prt) mf::LogVerbatim("CCTM")<<" Tried 2-plane match"<<" k "<<kpl<<":"<<match.Cls[kpl]
2315  <<":"<<match.End[kpl]<<" Chg "<<match.Chg[kpl]<<" Err "<<match.Err<<" match.oErr "<<match.oErr;
2316  if(match.Chg[kpl] <= 0) continue;
2317  if(match.Err > 10 || match.oErr > 10) continue;
2318  matcomb.push_back(match);
2319  } // !gotkcl
2320  } // jend
2321  } // iend
2322  } // jcl
2323  } // icl
2324  } // ipl
2325 
2326  if(matcomb.size() == 0) return;
2327 
2328  } // PlnMatch
2329 
2331  bool CCTrackMaker::DupMatch(MatchPars& match)
2332  {
2333 
2334  unsigned short nMatCl, nMiss;
2335  float toterr = match.Err + match.oErr;
2336  for(unsigned int imat = 0; imat < matcomb.size(); ++imat) {
2337  // check for exact matches
2338  if(match.Cls[0] == matcomb[imat].Cls[0] &&
2339  match.Cls[1] == matcomb[imat].Cls[1] &&
2340  match.Cls[2] == matcomb[imat].Cls[2]) {
2341 
2342  // compare the error
2343  if(toterr < matcomb[imat].Err + matcomb[imat].oErr) {
2344  // keep the better one
2345  matcomb[imat].End[0] = match.End[0];
2346  matcomb[imat].End[1] = match.End[1];
2347  matcomb[imat].End[2] = match.End[2];
2348  matcomb[imat].Vtx = match.Vtx;
2349  matcomb[imat].dWir = match.dWir;
2350  matcomb[imat].dAng = match.dAng;
2351  matcomb[imat].dX = match.dX;
2352  matcomb[imat].Err = match.Err;
2353  matcomb[imat].oVtx = match.oVtx;
2354  matcomb[imat].odWir = match.odWir;
2355  matcomb[imat].odAng = match.odAng;
2356  matcomb[imat].odX = match.odX;
2357  matcomb[imat].oErr = match.oErr;
2358  }
2359  return true;
2360  } // test
2361  // check for a 3-plane match vs 2-plane match
2362  nMatCl = 0;
2363  nMiss = 0;
2364  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
2365  if(match.Cls[ipl] >= 0) {
2366  if(match.Cls[ipl] == matcomb[imat].Cls[ipl] && (match.End[0] == matcomb[imat].End[0] || match.End[1] == matcomb[imat].End[1])) ++nMatCl;
2367  } else {
2368  ++nMiss;
2369  }
2370  } // ipl
2371  if(nMatCl == 2 && nMiss == 1) return true;
2372  } // imat
2373  return false;
2374  } // DupMatch
2375 
2377  void CCTrackMaker::SortMatches(art::FindManyP<recob::Hit> const& fmCluHits, unsigned short procCode)
2378  {
2379  // sort cluster matches by increasing total match error. Find the minimum total error of all
2380  // cluster match combinations and make tracks from them
2381  CluLen merr;
2382  std::vector<CluLen> materr;
2383  unsigned int ii, im;
2384 
2385  if(matcomb.size() == 0) return;
2386 
2387  // sort by decreasing error
2388  for(ii = 0; ii < matcomb.size(); ++ii) {
2389  merr.index = ii;
2390  merr.length = matcomb[ii].Err + matcomb[ii].oErr;
2391  materr.push_back(merr);
2392  } // ii
2393  std::sort(materr.begin(), materr.end(), lessThan);
2394 
2395  if(prt) {
2396  mf::LogVerbatim myprt("CCTM");
2397  myprt<<"SortMatches\n";
2398  myprt<<" ii im Vx Err dW dA dX oVx oErr odW odA odX Asym icl jcl kcl \n";
2399  for(ii = 0; ii < materr.size(); ++ii) {
2400  im = materr[ii].index;
2401  float asym = fabs(matcomb[im].Chg[0] - matcomb[im].Chg[1]) /
2402  (matcomb[im].Chg[0] + matcomb[im].Chg[1]);
2403  asym *= fabs(matcomb[im].Chg[1] - matcomb[im].Chg[2]) /
2404  (matcomb[im].Chg[1] + matcomb[im].Chg[2]);
2405  myprt<<std::fixed<<std::right
2406  <<std::setw(5)<<ii<<std::setw(5)<<im
2407  <<std::setw(4)<<matcomb[im].Vtx
2408  <<std::setw(7)<<std::setprecision(2)<<matcomb[im].Err
2409  <<std::setw(7)<<std::setprecision(1)<<matcomb[im].dWir
2410  <<std::setw(7)<<std::setprecision(2)<<matcomb[im].dAng
2411  <<std::setw(7)<<std::setprecision(2)<<matcomb[im].dX
2412  <<std::setw(4)<<matcomb[im].oVtx
2413  <<std::setw(7)<<std::setprecision(2)<<matcomb[im].oErr
2414  <<std::setw(7)<<std::setprecision(1)<<matcomb[im].odWir
2415  <<std::setw(7)<<std::setprecision(2)<<matcomb[im].odAng
2416  <<std::setw(7)<<std::setprecision(2)<<matcomb[im].odX
2417  <<std::setw(7)<<std::setprecision(3)<<asym
2418  <<" 0:"<<matcomb[im].Cls[0]<<":"<<matcomb[im].End[0]
2419  <<" 1:"<<matcomb[im].Cls[1]<<":"<<matcomb[im].End[1];
2420  if(nplanes > 2) myprt<<" 2:"<<matcomb[im].Cls[2]<<":"<<matcomb[im].End[2];
2421  myprt<<"\n";
2422  } // ii
2423  } // prt
2424 
2425  // define an array to ensure clusters are only used once
2426  std::array<std::vector<bool>, 3> pclUsed;
2427  unsigned short ipl;
2428  for(ipl = 0; ipl < nplanes; ++ipl) {
2429  pclUsed[ipl].resize(clsChain[ipl].size());
2430 // std::fill(pclUsed[ipl].begin(), pclUsed[ipl].end(), false);
2431  }
2432 
2433  // count the total number of clusters and length used in matcomb
2434  unsigned short matcombTotCl = 0;
2435  float matcombTotLen = 0;
2436  unsigned short icl;
2437  for(ii = 0; ii < matcomb.size(); ++ii) {
2438  for(ipl = 0; ipl < nplanes; ++ipl) {
2439  if(matcomb[ii].Cls[ipl] < 0) continue;
2440  icl = matcomb[ii].Cls[ipl];
2441  ++matcombTotCl;
2442  matcombTotLen += clsChain[ipl][icl].Length;
2443  }
2444  }
2445 
2446  if(prt) mf::LogVerbatim("CCTM")<<"Number of clusters to match "<<matcombTotCl <<" total length "<<matcombTotLen;
2447 
2448  if(matcombTotLen <= 0) {
2449  mf::LogError("CCTM")<<"SortMatches: bad matcomb total length "<<matcombTotLen;
2450  return;
2451  }
2452 
2453  // vector of matcomb indices of unique cluster matches
2454  std::vector<unsigned short> matIndex;
2455  // vector of matcomb indices of unique cluster matches that have the best total error
2456  std::vector<unsigned short> bestMatIndex;
2457  float totLen, totErr, bestTotErr = 9999;
2458  // start with the best match
2459  unsigned short jj, jm, nused, jcl;
2460  // fraction of the length of all clustters in matcomb that are used in a match
2461  float fracLen;
2462 
2463  for(ii = 0; ii < materr.size(); ++ii) {
2464  im = materr[ii].index;
2465  matIndex.clear();
2466  // skip really bad matches
2467  if(matcomb[im].Err > bestTotErr) continue;
2468  totLen = 0;
2469  // initialize pclUsed and flag the clusters in this match
2470  // mf::LogVerbatim("CCTM")<<"chk ii "<<ii<<" clusters "<<matcomb[im].Cls[0]<<" "<<matcomb[im].Cls[1]<<" "<<matcomb[im].Cls[2];
2471  for(ipl = 0; ipl < nplanes; ++ipl) {
2472  // initialize to no clusters used
2473  std::fill(pclUsed[ipl].begin(), pclUsed[ipl].end(), false);
2474  // check for 2 plane match
2475  if(matcomb[im].Cls[ipl] < 0) continue;
2476  icl = matcomb[im].Cls[ipl];
2477  pclUsed[ipl][icl] = true;
2478  totLen += clsChain[ipl][icl].Length;
2479  } // ipl
2480  // Initialize the error sum
2481  totErr = matcomb[im].Err;
2482  // Save the index
2483  matIndex.push_back(im);
2484  // look for matches in the rest of the list that are not already matched.
2485  for(jj = 0; jj < materr.size(); ++jj) {
2486  if(jj == ii) continue;
2487  jm = materr[jj].index;
2488  // skip really bad matches
2489  if(matcomb[jm].Err > bestTotErr) continue;
2490  // mf::LogVerbatim("CCTM")<<"chk match jj "<<jj<<" clusters "<<matcomb[jm].Cls[0]<<" "<<matcomb[jm].Cls[1]<<" "<<matcomb[jm].Cls[2];
2491  // check for non-unique cluster indices
2492  nused = 0;
2493  for(ipl = 0; ipl < nplanes; ++ipl) {
2494  if(matcomb[jm].Cls[ipl] < 0) continue;
2495  jcl = matcomb[jm].Cls[ipl];
2496  if(pclUsed[ipl][jcl]) ++nused;
2497  // This cluster chain was used in a previous match
2498  if(nused > 0) break;
2499  totLen += clsChain[ipl][jcl].Length;
2500  } // ipl
2501  // at least one of the clusters in this match have been used
2502  if(nused != 0) continue;
2503  // found a match with an unmatched set of clusters. Update the total error and flag them
2504  totErr += matcomb[jm].Err;
2505  matIndex.push_back(jm);
2506  // Flag the clusters used and see if all of them are used
2507  for(ipl = 0; ipl < nplanes; ++ipl) {
2508  if(matcomb[jm].Cls[ipl] < 0) continue;
2509  jcl = matcomb[jm].Cls[ipl];
2510  pclUsed[ipl][jcl] = true;
2511  } // ipl
2512  } // jm
2513  if(totLen == 0) continue;
2514  nused = 0;
2515  for(ipl = 0; ipl < nplanes; ++ipl) {
2516  for(unsigned short indx = 0; indx < pclUsed[ipl].size(); ++indx) if(pclUsed[ipl][indx]) ++nused;
2517  } // ipl
2518  if(totLen > matcombTotLen) std::cout<<"Oops "<<totLen<<" "<<matcombTotLen<<"\n";
2519  // weight the total error by the total length of all clusters
2520  fracLen = totLen / matcombTotLen;
2521 // totErr = totErr * nused / totLen;
2522 // totErr = totErr * matIndex.size() / fracLen;
2523  totErr /= fracLen;
2524  if(prt) {
2525  mf::LogVerbatim myprt("CCTM");
2526  myprt<<"match "<<im<<" totErr "<<totErr<<" nused "<<nused<<" fracLen "<<fracLen<<" totLen "<<totLen<<" mat: ";
2527  for(unsigned short indx = 0; indx < matIndex.size(); ++indx) myprt<<" "<<matIndex[indx];
2528  } // prt
2529  // check for more used clusters and a better total error
2530 // if(totErr < bestTotErr) {
2531  if(totErr < bestTotErr) {
2532  bestTotErr = totErr;
2533  bestMatIndex = matIndex;
2534  if(nused == matcombTotCl) break;
2535  if(prt) {
2536  mf::LogVerbatim myprt("CCTM");
2537  myprt<<"bestTotErr "<<bestTotErr<<" nused "<<nused<<" matcombTotCl "<<matcombTotCl<<" mat: ";
2538  for(unsigned short indx = 0; indx < bestMatIndex.size(); ++indx) myprt<<" "<<bestMatIndex[indx];
2539  } // prt
2540  // stop looking if we have found everything
2541  if(fracLen > 0.999) break;
2542  } // totErr < bestTotErr
2543  } // im
2544 
2545  if(bestTotErr > 9000) return;
2546 
2547  for(ii = 0; ii < bestMatIndex.size(); ++ii) {
2548  im = bestMatIndex[ii];
2549  // if(prt) mf::LogVerbatim("CCTM")<<"SM: "<<ii<<" im "<<im<<" 0:"<<matcomb[im].Cls[0]<<":"<<matcomb[im].End[0]<<" 1:"<<matcomb[im].Cls[1]<<":"<<matcomb[im].End[1]<<" 2:"<<matcomb[im].Cls[2]<<":"<<matcomb[im].End[2];
2550  // std::cout<<"FillTrkHits "<<ii<<"\n";
2551  FillTrkHits(fmCluHits, im);
2552  // look for missing clusters?
2553  // store this track with processor code 1
2554  StoreTrack(fmCluHits, im, procCode);
2555  } // ii
2556 
2557  } // SortMatches
2558 
2560  void CCTrackMaker::FillEndMatch2(MatchPars& match)
2561  {
2562  // 2D version of FillEndMatch
2563 
2564  match.Err = 100;
2565  match.oErr = 100;
2566  match.Chg[2] = 0;
2567  match.dWir = 0; match.dAng = 0;
2568  match.odWir = 0; match.odAng = 0;
2569 
2570  unsigned short ipl = 0;
2571  unsigned short jpl = 1;
2572 // mf::LogVerbatim("CCTM")<<"chk "<<ipl<<":"<<match.Cls[ipl]<<":"<<match.End[ipl]<<" and "<<jpl<<":"<<match.Cls[jpl]<<":"<<match.End[jpl];
2573 
2574  if(match.Cls[0] < 0 || match.Cls[1] < 0) return;
2575 
2576  unsigned short icl = match.Cls[ipl];
2577  unsigned short iend = match.End[ipl];
2578  match.Chg[ipl] = clsChain[ipl][icl].TotChg;
2579  // cluster i match end
2580  float miX = clsChain[ipl][icl].X[iend];
2581  // cluster i other end
2582  unsigned short oiend = 1 - iend;
2583  float oiX = clsChain[ipl][icl].X[oiend];
2584 
2585  unsigned short jcl = match.Cls[jpl];
2586  unsigned short jend = match.End[jpl];
2587  match.Chg[jpl] = clsChain[jpl][jcl].TotChg;
2588  // cluster j match end
2589  float mjX = clsChain[jpl][jcl].X[jend];
2590  // cluster j other end
2591  unsigned short ojend = 1 - jend;
2592  float ojX = clsChain[jpl][jcl].X[ojend];
2593 
2594  // look for a match end vertex match
2595  match.Vtx = -1;
2596  if(clsChain[ipl][icl].VtxIndex[iend] >= 0 &&
2597  clsChain[ipl][icl].VtxIndex[iend] == clsChain[jpl][jcl].VtxIndex[jend]) {
2598  match.Vtx = clsChain[ipl][icl].VtxIndex[iend];
2599  miX = vtx[match.Vtx].X;
2600  mjX = vtx[match.Vtx].X;
2601  }
2602 
2603  // look for an other end vertex match
2604  match.oVtx = -1;
2605  if(clsChain[ipl][icl].VtxIndex[oiend] >= 0 &&
2606  clsChain[ipl][icl].VtxIndex[oiend] == clsChain[jpl][jcl].VtxIndex[ojend]) {
2607  match.oVtx = clsChain[ipl][icl].VtxIndex[oiend];
2608  oiX = vtx[match.oVtx].X;
2609  ojX = vtx[match.oVtx].X;
2610  }
2611 
2612  // find the charge asymmetry
2613  float chgAsym = 1;
2614  if(fChgAsymFactor[algIndex] > 0) {
2615  chgAsym = fabs(match.Chg[ipl] - match.Chg[jpl]) / (match.Chg[ipl] + match.Chg[jpl]);
2616  if(chgAsym > 0.5) return;
2617  chgAsym = 1 + fChgAsymFactor[algIndex] * chgAsym;
2618  }
2619 
2620  // find the error at the match end
2621  float maxSlp = fabs(clsChain[ipl][icl].Slope[iend]);
2622  if(fabs(clsChain[jpl][jcl].Slope[jend]) > maxSlp) maxSlp = fabs(clsChain[jpl][jcl].Slope[jend]);
2623  float sigmaX = fXMatchErr[algIndex] + std::max(maxSlp, (float)20);
2624  match.dX = fabs(miX - mjX);
2625  match.Err = chgAsym * match.dX / sigmaX;
2626 
2627 
2628  // find the error at the other end
2629  maxSlp = fabs(clsChain[ipl][icl].Slope[oiend]);
2630  if(fabs(clsChain[jpl][jcl].Slope[ojend]) > maxSlp) maxSlp = fabs(clsChain[jpl][jcl].Slope[ojend]);
2631  sigmaX = fXMatchErr[algIndex] + std::max(maxSlp, (float)20);
2632  match.odX = fabs(oiX - ojX);
2633  match.oErr = chgAsym * match.odX / sigmaX;
2634 
2635  if(prt) mf::LogVerbatim("CCTM")<<"FEM2: m "<<ipl<<":"<<icl<<":"<<iend<<" miX "<<miX
2636  <<" - "<<jpl<<":"<<jcl<<":"<<jend<<" mjX "<<mjX<<" match.dX "<<match.dX
2637  <<" match.Err "<<match.Err<<" chgAsym "<<chgAsym<<" o "<<" oiX "<<oiX
2638  <<" ojX "<<ojX<<" match.odX "<<match.odX<<" match.oErr "<<match.oErr<<"\n";
2639 
2640 
2641 } // FillEndMatch2
2642 
2644  void CCTrackMaker::FillEndMatch(MatchPars& match)
2645  {
2646  // fill the matching parameters for this cluster match. The calling routine
2647  // should set the match end vertex ID (if applicable) as well as the
2648  // cluster IDs and matching ends in each plane. Note that the matching variables
2649  // Note that dWir, dAng and dTim are not filled if there is a vertex (match.Vtx >= 0).
2650  // Likewise, odWir, odAng and odX are not filled if there is a vertex match
2651  // at the other end
2652 
2653  if(nplanes == 2) {
2654  FillEndMatch2(match);
2655  return;
2656  }
2657 
2658  std::array<short, 3> mVtx;
2659  std::array<short, 3> oVtx;
2660  std::array<float, 3> oWir;
2661  std::array<float, 3> oSlp;
2662  std::array<float, 3> oAng;
2663  std::array<float, 3> oX;
2664 
2665  std::array<float, 3> mChg;
2666 
2667  unsigned short ii, ipl, iend, jpl, jend, kpl, kend, oend;
2668  short icl, jcl, kcl;
2669 
2670  for(ipl = 0; ipl < 3; ++ipl) {
2671  mVtx[ipl] = -1; oVtx[ipl] = -1;
2672  oWir[ipl] = -66; oSlp[ipl] = -66; oAng[ipl] = -66; oX[ipl] = -66;
2673  mChg[ipl] = -1;
2674  } // ipl
2675 
2676  // initialize parameters that shouldn't have been set by the calling routine
2677  match.dWir = 0; match.dAng = 0; match.dX = 0; match.Err = 100;
2678  match.odWir = 0; match.odAng = 0; match.odX = 0; match.oErr = 100;
2679  match.oVtx = -1;
2680 
2681  if(prt) {
2682  mf::LogVerbatim myprt("CCTM");
2683  myprt<<"FEM ";
2684  for(ipl = 0; ipl < nplanes; ++ipl) {
2685  myprt<<" "<<ipl<<":"<<match.Cls[ipl]<<":"<<match.End[ipl];
2686  }
2687  }
2688 
2689  short missingPlane = -1;
2690  unsigned short nClInPln = 0;
2691  // number of vertex matches at each end
2692  short aVtx = -1;
2693  unsigned short novxmat = 0;
2694  short aoVtx = -1;
2695  unsigned short nvxmat = 0;
2696  unsigned short nShortCl = 0;
2697  // fill the other end parameters in each plane
2698  for(ipl = 0; ipl < nplanes; ++ipl) {
2699  if(match.Cls[ipl] < 0) {
2700  missingPlane = ipl;
2701  continue;
2702  }
2703  ++nClInPln;
2704  icl = match.Cls[ipl];
2705  match.Chg[ipl] = clsChain[ipl][icl].TotChg;
2706  mChg[ipl] = clsChain[ipl][icl].TotChg;
2707  iend = match.End[ipl];
2708  mVtx[ipl] = clsChain[ipl][icl].VtxIndex[iend];
2709  if(clsChain[ipl][icl].Length < 6) ++nShortCl;
2710  if(mVtx[ipl] >= 0) {
2711  if(aVtx < 0) aVtx = mVtx[ipl];
2712  if(mVtx[ipl] == aVtx) ++nvxmat;
2713  }
2714  if(prt ) mf::LogVerbatim("CCTM")<<"FEM: m "<<ipl<<":"<<icl<<":"<<iend<<" Vtx "<<mVtx[ipl]<<" Wir "<<clsChain[ipl][icl].Wire[iend]<<std::fixed<<std::setprecision(3)<<" Slp "<<clsChain[ipl][icl].Slope[iend]<<std::fixed<<std::setprecision(1)<<" X "<<clsChain[ipl][icl].X[iend];
2715 
2716  oend = 1 - iend;
2717  oWir[ipl] = clsChain[ipl][icl].Wire[oend];
2718  oAng[ipl] = clsChain[ipl][icl].Angle[oend];
2719  oSlp[ipl] = clsChain[ipl][icl].Slope[oend];
2720  oX[ipl] = clsChain[ipl][icl].X[oend];
2721  oVtx[ipl] = clsChain[ipl][icl].VtxIndex[oend];
2722  if(oVtx[ipl] >= 0) {
2723  if(aoVtx < 0) aoVtx = oVtx[ipl];
2724  if(oVtx[ipl] == aoVtx) ++novxmat;
2725  }
2726 
2727  if(prt) mf::LogVerbatim("CCTM")<<" o "<<ipl<<":"<<icl<<":"<<oend<<" oVtx "<<oVtx[ipl]<<" oWir "<<oWir[ipl]<<std::fixed<<std::setprecision(3)<<" oSlp "<<oSlp[ipl]<<std::fixed<<std::setprecision(1)<<" oX "<<oX[ipl]<<" Chg "<<(int)mChg[ipl];
2728 
2729  } // ipl
2730 
2731  bool isShort = (nShortCl > 1);
2732 
2733  if(nClInPln < 2) {
2734  mf::LogWarning("CCTM")<<"Not enough matched planes supplied";
2735  return;
2736  }
2737 
2738  if(prt) mf::LogVerbatim("CCTM")<<"FEM: Vtx m "<<aVtx<<" count "<<nvxmat
2739  <<" o "<<aoVtx<<" count "<<novxmat
2740  <<" missingPlane "<<missingPlane
2741  <<" nClInPln "<<nClInPln;
2742 
2743  // perfect match
2744  if(nvxmat == 3 && novxmat == 3) {
2745  match.Vtx = aVtx; match.Err = 0;
2746  match.oVtx = aoVtx; match.oErr = 0;
2747  return;
2748  }
2749 
2750  // 2-plane vertex match?
2751  // factors applied to error = 1 (no vtx), 0.5 (2 pln vtx), 0.33 (3 pln vtx)
2752  float vxFactor = 1;
2753  float ovxFactor = 1;
2754  if(nClInPln == 3) {
2755  // a cluster in all 3 planes
2756  if(nvxmat == 3) {
2757  // and all vertex assignments agree at the match end
2758  match.Vtx = aVtx; vxFactor = 0.33;
2759  }
2760  if(novxmat == 3) {
2761  // and all vertex assignments agree at the other end
2762  match.oVtx = aoVtx; ovxFactor = 0.33;
2763  }
2764  } else {
2765  // a cluster in 2 planes
2766  if(nvxmat == 2) {
2767  match.Vtx = aVtx; vxFactor = 0.5;
2768  }
2769  if(novxmat == 2) {
2770  match.oVtx = aoVtx; ovxFactor = 0.5;
2771  }
2772  } // nClInPln
2773 
2774  // find wire, X and Time at both ends
2775 
2776  // Find the "other end" matching parameters with
2777  // two cases: a 3-plane match or a 2-plane match
2778  // and with/without an other end vertex
2779 
2780  double ypos, zpos;
2781  float kWir, okWir;
2782  float kSlp, okSlp, kAng, okAng, okX, kX, kTim, okTim;
2783 
2784  if(nClInPln == 3) {
2785  ipl = 0; jpl = 1; kpl = 2;
2786  } else {
2787  // 2-plane match
2788  kpl = missingPlane;
2789  if(kpl == 0) {
2790  ipl = 1; jpl = 2;
2791  } else if(kpl == 1) {
2792  ipl = 2; jpl = 0;
2793  } else {
2794  ipl = 0; jpl = 1;
2795  } // kpl test
2796  } // missing plane
2797  iend = match.End[ipl]; jend = match.End[jpl];
2798  icl = match.Cls[ipl]; jcl = match.Cls[jpl];
2799  if(nplanes > 2) {
2800  kcl = match.Cls[kpl];
2801  kend = match.End[kpl];
2802  }
2803 
2805  okSlp = geom->ThirdPlaneSlope(ipl, oSlp[ipl], jpl, oSlp[jpl], tpc, cstat);
2806  okAng = atan(okSlp);
2807  // handle the case near pi/2, where the errors on large slopes could result in
2808  // a wrong-sign kAng
2809  bool ignoreSign = (fabs(okSlp) > 10);
2810  if(ignoreSign) okAng = fabs(okAng);
2811  if(match.oVtx >= 0) {
2812  // a vertex exists at the other end
2813  okWir = geom->WireCoordinate(vtx[match.oVtx].Y, vtx[match.oVtx].Z, kpl, tpc, cstat);
2814  okX = vtx[match.oVtx].X;
2815  } else {
2816  // no vertex at the other end
2817  geom->IntersectionPoint(oWir[ipl], oWir[jpl],ipl, jpl, cstat, tpc, ypos, zpos);
2818  okWir = (0.5 + geom->WireCoordinate(ypos, zpos, kpl, tpc, cstat));
2819  okX = 0.5 * (oX[ipl] + oX[jpl]);
2820  }
2821  okTim = detprop->ConvertXToTicks(okX, kpl, tpc, cstat);
2822  if(prt) mf::LogVerbatim("CCTM")<<"FEM: oEnd"<<" kpl "<<kpl<<" okSlp "<<okSlp<<" okAng "
2823  <<okAng<<" okWir "<<(int)okWir<<" okX "<<okX;
2824 
2825 
2827 
2828  kSlp = geom->ThirdPlaneSlope(ipl, clsChain[ipl][icl].Slope[iend], jpl, clsChain[jpl][jcl].Slope[jend], tpc, cstat);
2829  kAng = atan(kSlp);
2830  if(ignoreSign) kAng = fabs(kAng);
2831  if(match.Vtx >= 0) {
2832  if(vtx.size() == 0 || (unsigned int)match.Vtx > vtx.size() - 1) {
2833  mf::LogError("CCTM")<<"FEM: Bad match.Vtx "<<match.Vtx<<" vtx size "<<vtx.size();
2834  return;
2835  }
2836  // a vertex exists at the match end
2837  kWir = geom->WireCoordinate(vtx[match.Vtx].Y, vtx[match.Vtx].Z, kpl, tpc, cstat);
2838  kX = vtx[match.Vtx].X;
2839  } else {
2840  // no vertex at the match end
2841  geom->IntersectionPoint(clsChain[ipl][icl].Wire[iend], clsChain[jpl][jcl].Wire[jend], ipl, jpl, cstat, tpc, ypos, zpos);
2842  kWir = (0.5 + geom->WireCoordinate(ypos, zpos, kpl, tpc, cstat));
2843  kX = 0.5 * (clsChain[ipl][icl].X[iend] + clsChain[jpl][jcl].X[jend]);
2844  }
2845  kTim = detprop->ConvertXToTicks(kX, kpl, tpc, cstat);
2846  if(prt) mf::LogVerbatim("CCTM")<<"FEM: mEnd"<<" kpl "<<kpl<<" kSlp "<<kSlp<<" kAng "<<kAng<<" kX "<<kX;
2847 
2848  // try to find a 3-plane match using this information
2849  if(nClInPln < 3 && FindMissingCluster(kpl, kcl, kend, kWir, kX, okWir, okX)) {
2850  nClInPln = 3;
2851  // update local variables
2852  match.Cls[kpl] = kcl;
2853  match.End[kpl] = kend;
2854  match.Chg[kpl] = clsChain[kpl][kcl].TotChg;
2855  mChg[kpl] = clsChain[kpl][kcl].TotChg;
2856  oend = 1 - kend;
2857  oWir[kpl] = clsChain[kpl][kcl].Wire[oend];
2858  oX[kpl] = clsChain[kpl][kcl].X[oend];
2859  oAng[kpl] = clsChain[kpl][kcl].Angle[oend];
2860  oSlp[kpl] = clsChain[kpl][kcl].Slope[oend];
2861  } // FindMissingCluster
2862 
2863  // decide whether to continue with a 2-plane match. The distance between match and other end should
2864  // be large enough to create a cluster
2865  if(nClInPln == 2 && fabs(okWir - kWir) > 3) return;
2866 
2867  // Calculate the cluster charge asymmetry. This factor will be applied
2868  // to the error of the end matches
2869  float chgAsym = 1;
2870  // Get the charge in the plane without a matching cluster
2871  if(nClInPln < 3 && mChg[missingPlane] <= 0) {
2872  if(missingPlane != kpl) mf::LogError("CCTM")<<"FEM bad missingPlane "<<missingPlane<<" "<<kpl<<"\n";
2873  mChg[kpl] = ChargeNear(kpl, (unsigned short)kWir, kTim, (unsigned short)okWir, okTim);
2874  match.Chg[kpl] = mChg[kpl];
2875  if(prt) mf::LogVerbatim("CCTM")<<"FEM: Missing cluster in "<<kpl<<" ChargeNear "<<(int)kWir<<":"<<(int)kTim
2876  <<" "<<(int)okWir<<":"<<(int)okTim<<" chg "<<mChg[kpl];
2877  if(mChg[kpl] <= 0) return;
2878  }
2879 
2880  if(fChgAsymFactor[algIndex] > 0) {
2881  chgAsym = ChargeAsym(mChg);
2882  if(chgAsym > 0.5) return;
2883  chgAsym = 1 + fChgAsymFactor[algIndex] * chgAsym;
2884  }
2885 
2886  if(prt) mf::LogVerbatim("CCTM")<<"FEM: charge asymmetry factor "<<chgAsym;
2887  float sigmaX, sigmaA;
2888  float da, dx, dw;
2889 
2891  // check for vertex consistency at the match end
2892  aVtx = -1;
2893  bool allPlnVtxMatch = false;
2894  if(nClInPln == 3) {
2895  unsigned short nmvtx = 0;
2896  for(ii = 0; ii < nplanes; ++ii) {
2897  if(mVtx[ii] >= 0) {
2898  if(aVtx < 0) aVtx = mVtx[ii];
2899  ++nmvtx;
2900  }
2901  } // ii
2902  // same vertex in all planes
2903  if(nmvtx ) allPlnVtxMatch = true;
2904  } // nClInPln
2905 
2906  // inflate the X match error to allow for missing one wire on the end of a cluster
2907  sigmaX = fXMatchErr[algIndex] + std::max(kSlp, (float)20);
2908  sigmaA = fAngleMatchErr[algIndex] * AngleFactor(kSlp);
2909  if(prt) mf::LogVerbatim("CCTM")<<"bb "<<algIndex<<" "<<fXMatchErr[algIndex]<<" "<<fAngleMatchErr[algIndex]<<" kslp "<<kSlp;
2910 
2911  if(nClInPln == 3) {
2912  kcl = match.Cls[kpl];
2913  kend = match.End[kpl];
2914  dw = kWir - clsChain[kpl][kcl].Wire[kend];
2915  match.dWir = dw;
2916  if(fabs(match.dWir) > 100) return;
2917  if(match.Vtx >= 0) {
2918  match.dX = kX - clsChain[kpl][kcl].X[kend];
2919  } else {
2920  match.dX = std::abs(clsChain[ipl][icl].X[iend] - clsChain[jpl][jcl].X[jend]) +
2921  std::abs(clsChain[ipl][icl].X[iend] - clsChain[kpl][kcl].X[kend]);
2922  }
2923  if(prt) mf::LogVerbatim("CCTM")<<" dw "<<dw<<" dx "<<match.dX;
2924  // TODO: Angle matching has problems with short clusters
2925  if(!isShort) {
2926  if(ignoreSign) {
2927  match.dAng = kAng - fabs(clsChain[kpl][kcl].Angle[kend]);
2928  } else {
2929  match.dAng = kAng - clsChain[kpl][kcl].Angle[kend];
2930  }
2931  } // !isShort
2932  da = fabs(match.dAng) / sigmaA;
2933  dx = fabs(match.dX) / sigmaX;
2934  if(allPlnVtxMatch) {
2935  // matched vertex. Use angle for match error
2936  match.Err = vxFactor * chgAsym * da / 3;
2937  if(prt) mf::LogVerbatim("CCTM")<<" 3-pln w Vtx match.Err "<<match.Err;
2938  } else {
2939  dw /= 2;
2940  // divide by 9
2941  match.Err = vxFactor * chgAsym * sqrt(dx*dx + da*da + dw*dw) / 9;
2942  if(prt) mf::LogVerbatim("CCTM")<<" 3-pln match.Err "<<match.Err;
2943  }
2944  } else {
2945  // 2-plane match
2946  match.dWir = -1;
2947  match.dAng = -1;
2948  match.dX = clsChain[ipl][icl].X[iend] - clsChain[jpl][jcl].X[jend];
2949  // degrade error by 3 for 2-plane matches
2950  match.Err = 3 + vxFactor * chgAsym * fabs(match.dX) / sigmaX;
2951  if(prt) mf::LogVerbatim("CCTM")<<" 2-pln Err "<<match.Err;
2952  } // !(nClInPln == 3)
2953 
2955  if(nClInPln == 3) {
2956  // A cluster in all 3 planes
2957  dw = okWir - oWir[kpl];
2958  match.odWir = dw;
2959  if(match.oVtx >= 0) {
2960  match.odX = okX - oX[kpl];
2961  } else {
2962  match.odX = std::abs(oX[ipl] - oX[jpl]) + std::abs(oX[ipl] - oX[kpl]);
2963  }
2964  if(prt) mf::LogVerbatim("CCTM")<<" odw "<<match.odWir<<" odx "<<match.odX<<" sigmaX "<<sigmaX;
2965  // TODO: CHECK FOR SHOWER-LIKE CLUSTER OTHER END MATCH
2966  if(!isShort) {
2967  if(ignoreSign) {
2968  match.odAng = okAng - fabs(oAng[kpl]);
2969  } else {
2970  match.odAng = okAng - oAng[kpl];
2971  }
2972  } // !isShort
2973  da = match.odAng / sigmaA;
2974  dx = fabs(match.odX) / sigmaX;
2975  // error for wire number match
2976  dw /= 2;
2977  // divide by number of planes with clusters * 3 for dx, da and dw
2978  match.oErr = ovxFactor * chgAsym * sqrt(dx*dx + da*da + dw*dw) / 9;
2979  if(prt) mf::LogVerbatim("CCTM")<<" 3-pln match.oErr "<<match.oErr;
2980  } else {
2981  // Only 2 clusters in 3 planes
2982  match.odX = (oX[ipl] - oX[jpl]) / sigmaX;
2983  match.oErr = 3 + ovxFactor * chgAsym * fabs(match.odX);
2984  if(prt) mf::LogVerbatim("CCTM")<<" 2-pln match.oErr "<<match.oErr;
2985  }
2986  // std::cout<<"FEM done\n";
2987 
2988  /*
2989  if(nClInPln == 3 && fabs(match.dAng) < 20) {
2990  if(fabs(match.dX) > 0.5) mf::LogVerbatim("CCTM")<<"Bad dX "<<match.dX<<" "<<dx<<" clusters "<<ipl<<":"<<icl<<" "<<jpl<<":"<<jcl<<" "<<kpl<<":"<<kcl<<"\n";
2991  // temp code to grep for ntuple elements in the output
2992  mf::LogVerbatim("CCTM")<<"ntup "<<kSlp<<" "<<match.dX<<" "<<chgAsym<<" "<<match.dAng<<" "<<match.dX<<" "<<match.dWir<<" "<<match.Err<<" "<<match.odAng<<" "<<match.odX<<" "<<match.odWir<<" "<<match.oErr;
2993  if(chgAsym > 1.5) mf::LogVerbatim("CCTM")<<"BadAsym "<<"0:"<<match.Cls[0]<<":"<<match.End[0]<<" 1:"<<match.Cls[1]<<":"<<match.End[1]<<" 2:"<<match.Cls[2]<<":"<<match.End[2];
2994  }
2995  */
2996  } // FillEndMatch
2997 
2998 
3000  void CCTrackMaker::FindMidPointMatch(art::FindManyP<recob::Hit> const& fmCluHits, MatchPars& match, unsigned short kkpl, unsigned short kkcl, unsigned short kkend, float& kkWir, float& kkX)
3001  {
3002  // Cluster chain kkcl is broken due to a failure in MakeClusterChains. Find the intersection of
3003  // the other two clusters in match at the appropriate end of cluster kkcl
3004 
3005  kkWir = -1;
3006  // find the match point at the other end of the original match point
3007  kkend = 1 - kkend;
3008  // mf::LogVerbatim("CCTM")<<"FMPM "<<kkpl<<":"<<kkcl<<":"<<kkend;
3009  kkX = clsChain[kkpl][kkcl].X[kkend];
3010  float matchTime = detprop->ConvertXToTicks(kkX, kkpl, tpc, cstat);
3011 
3012  // vector of wire numbers that have the most similar time
3013  std::vector<unsigned int> wirs;
3014  std::vector<unsigned int> plns;
3015  std::vector<art::Ptr<recob::Hit>> clusterhits;
3016  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
3017  if(ipl == kkpl) continue;
3018  // this shouldn't happen but check anyway
3019  if(match.Cls[ipl] < 0) continue;
3020  float dTime, best = 99999;
3021  unsigned short wire = 0;
3022  unsigned short icl, ccl = match.Cls[ipl];
3023  // loop over all clusters in the chain
3024  for(unsigned short ii = 0; ii < clsChain[kkpl][ccl].ClsIndex.size(); ++ii) {
3025  icl = clsChain[kkpl][ccl].ClsIndex[ii];
3026  if(cls[ipl][icl].EvtIndex > fmCluHits.size() -1) {
3027  std::cout<<"Bad ClsIndex in FMPM\n";
3028  exit(1);
3029  }
3030  clusterhits = fmCluHits.at(cls[ipl][icl].EvtIndex);
3031  // loop over all the hits in each cluster
3032  for(unsigned short iht = 0; iht < clusterhits.size(); ++iht) {
3033  dTime = fabs(clusterhits[iht]->PeakTime() - matchTime);
3034  if(dTime < best) {
3035  best = dTime;
3036  wire = clusterhits[iht]->WireID().Wire;
3037  }
3038  } // iht
3039  } // ii
3040  wirs.push_back(wire);
3041  plns.push_back(ipl);
3042  // mf::LogVerbatim("CCTM")<<" ipl "<<ipl<<" wire "<<wire;
3043  } // ipl
3044  if(wirs.size() != 2) return;
3045  double Y, Z;
3046  geom->IntersectionPoint(wirs[0], wirs[1], plns[0], plns[1], cstat, tpc, Y, Z);
3047  kkWir = geom->WireCoordinate(Y, Z, kkpl, tpc, cstat);
3048 
3049  } // FindMidPointMatch
3050 
3052  bool CCTrackMaker::FindMissingCluster(unsigned short kpl, short& kcl, unsigned short& kend, float kWir, float kX, float okWir, float okX)
3053  {
3054  // try to attach a missing cluster to the cluster chain kcl. kend is the "match end"
3055 
3056  unsigned short okend;
3057  float dxcut;
3058 
3059  if(kcl >= 0) return false;
3060 
3061  // Look for a missing cluster with loose cuts
3062  float kslp = fabs((okX - kX) / (okWir - kWir));
3063  if(kslp > 20) kslp = 20;
3064  // expand dX cut assuming there is a missing hit on the end of a cluster => 1 wire
3065  dxcut = 3 * fXMatchErr[algIndex] + kslp;
3066  unsigned short nfound = 0;
3067  unsigned short foundCl = 0, foundEnd = 0;
3068  for(unsigned short ccl = 0; ccl < clsChain[kpl].size(); ++ccl) {
3069  if(clsChain[kpl][ccl].InTrack >= 0) continue;
3070  // require a match at both ends
3071  for(unsigned short end = 0; end < 2; ++end) {
3072  okend = 1 - end;
3073  if(fabs(clsChain[kpl][ccl].Wire[end] - kWir) > 4) continue;
3074  if(fabs(clsChain[kpl][ccl].Wire[okend] - okWir) > 4) continue;
3075  // mf::LogVerbatim("CCTM")<<"FMC chk "<<kpl<<":"<<ccl<<":"<<end<<" dW "<<clsChain[kpl][ccl].Wire[end] - kWir<<" odW "<<clsChain[kpl][ccl].Wire[okend] - okWir<<" ccl X "<<clsChain[kpl][ccl].X[end]<<" kX "<<kX<<" ccl oX "<<clsChain[kpl][ccl].X[okend]<<" okX "<<okX<<" dxcut "<<dxcut;
3076  // require at least one end to match
3077  if(fabs(clsChain[kpl][ccl].X[end] - kX) > dxcut && fabs(clsChain[kpl][ccl].X[okend] - okX) > dxcut) continue;
3078  ++nfound;
3079  foundCl = ccl;
3080  foundEnd = end;
3081  } // end
3082  } // ccl
3083  if(nfound == 0) return false;
3084  if(nfound > 1) {
3085  mf::LogVerbatim("CCTM")<<"FindMissingCluster: Found too many matches. Write some code "<<nfound;
3086  return false;
3087  }
3088  kcl = foundCl;
3089  kend = foundEnd;
3090  return true;
3091 
3092  } // FindMissingCluster
3093 
3095  float CCTrackMaker::ChargeAsym(std::array<float, 3>& mChg)
3096  {
3097  // find charge asymmetry between the cluster with the highest (lowest)
3098  // charge
3099  float big = 0, small = 1.E9;
3100  for(unsigned short ii = 0; ii < 3; ++ii) {
3101  if(mChg[ii] < small) small = mChg[ii];
3102  if(mChg[ii] > big) big = mChg[ii];
3103  }
3104  // chgAsym varies between 0 (perfect charge match) and 1 (dreadfull)
3105  return (big - small) / (big + small);
3106  } // CalculateChargeAsym
3107 
3108 
3110  void CCTrackMaker::FillTrkHits(art::FindManyP<recob::Hit> const& fmCluHits, unsigned short imat)
3111  {
3112  // Fills the trkHits vector using cluster hits associated with the match combo imat
3113 
3114  unsigned short ipl;
3115 
3116  for(ipl = 0; ipl < 3; ++ipl) trkHits[ipl].clear();
3117 
3118  if(imat > matcomb.size()) return;
3119 
3120  unsigned short indx;
3121  std::vector<art::Ptr<recob::Hit>> clusterhits;
3122  unsigned short icc, ccl, icl, ecl, iht, ii;
3123  short endOrder, fillOrder;
3124 
3125  if(prt) mf::LogVerbatim("CCTM")<<"In FillTrkHits: matcomb "<<imat<<" cluster chains "<<matcomb[imat].Cls[0]<<" "<<matcomb[imat].Cls[1]<<" "<<matcomb[imat].Cls[2];
3126 
3127  for(ipl = 0; ipl < nplanes; ++ipl) {
3128  if(matcomb[imat].Cls[ipl] < 0) continue;
3129  // ccl is the cluster chain index
3130  ccl = matcomb[imat].Cls[ipl];
3131  // endOrder = 1 for normal order (hits added from US to DS), and -1 for reverse order
3132  endOrder = 1 - 2 * matcomb[imat].End[ipl];
3133  // re-order the sequence of cluster indices for reverse order
3134  if(endOrder < 0) {
3135  std::reverse(clsChain[ipl][ccl].ClsIndex.begin(), clsChain[ipl][ccl].ClsIndex.end());
3136  std::reverse(clsChain[ipl][ccl].Order.begin(), clsChain[ipl][ccl].Order.end());
3137  for(ii = 0; ii < clsChain[ipl][ccl].Order.size(); ++ii) clsChain[ipl][ccl].Order[ii] = 1 - clsChain[ipl][ccl].Order[ii];
3138  }
3139  if(ccl > clsChain[ipl].size() - 1) {
3140  mf::LogError("CCTM")<<"Bad cluster chain index "<<ccl<<" in plane "<<ipl;
3141  continue;
3142  }
3143  // loop over all the clusters in the chain
3144  for(icc = 0; icc < clsChain[ipl][ccl].ClsIndex.size(); ++icc) {
3145  icl = clsChain[ipl][ccl].ClsIndex[icc];
3146  if(icl > fmCluHits.size() - 1) {
3147  std::cout<<"oops in FTH "<<icl<<" clsChain size "<<clsChain[ipl].size()<<"\n";
3148  exit(1);
3149  }
3150  ecl = cls[ipl][icl].EvtIndex;
3151  if(ecl > fmCluHits.size() - 1) {
3152  std::cout<<"FTH bad EvtIndex "<<ecl<<" fmCluHits size "<<fmCluHits.size()<<"\n";
3153  continue;
3154  }
3155  clusterhits = fmCluHits.at(ecl);
3156  if(clusterhits.size() == 0) {
3157  std::cout<<"FTH no cluster hits for EvtIndex "<<cls[ipl][icl].EvtIndex<<"\n";
3158  continue;
3159  }
3160  indx = trkHits[ipl].size();
3161  trkHits[ipl].resize(indx + clusterhits.size());
3162  // ensure the hit fill ordering is consistent
3163  fillOrder = 1 - 2 * clsChain[ipl][ccl].Order[icc];
3164 // mf::LogVerbatim("CCTM")<<"FillOrder ipl "<<ipl<<" ccl "<<ccl<<" icl "<<icl<<" endOrder "<<endOrder<<" fillOrder "<<fillOrder;
3165  if(fillOrder == 1) {
3166 // mf::LogVerbatim("CCTM")<<" first hit "<<clusterhits[0]->WireID().Wire<<":"<<(int)clusterhits[0]->PeakTime();
3167  for(iht = 0; iht < clusterhits.size(); ++iht) {
3168  if(indx + iht > trkHits[ipl].size() - 1) std::cout<<"FTH oops3\n";
3169  trkHits[ipl][indx + iht] = clusterhits[iht];
3170  }
3171  } else {
3172 // iht = clusterhits.size() - 1;
3173 // mf::LogVerbatim("CCTM")<<" first hit "<<clusterhits[iht]->WireID().Wire<<":"<<(int)clusterhits[iht]->PeakTime();
3174  for(ii = 0; ii < clusterhits.size(); ++ii) {
3175  iht = clusterhits.size() - 1 - ii;
3176  if(indx + ii > trkHits[ipl].size() - 1) std::cout<<"FTH oops4\n";
3177  trkHits[ipl][indx + ii] = clusterhits[iht];
3178  } // ii
3179  }
3180  } // icc
3181  ii = trkHits[ipl].size() - 1;
3182  if(prt) mf::LogVerbatim("CCTM")<<"plane "<<ipl<<" first p "<<trkHits[ipl][0]->WireID().Plane<<" w "<<trkHits[ipl][0]->WireID().Wire<<":"<<(int)trkHits[ipl][0]->PeakTime()<<" last p "<<trkHits[ipl][ii]->WireID().Plane<<" w "<<trkHits[ipl][ii]->WireID().Wire<<":"<<(int)trkHits[ipl][ii]->PeakTime();
3183 // for(ii = 0; ii < trkHits[ipl].size(); ++ii) mf::LogVerbatim("CCTM")<<ii<<" p "<<trkHits[ipl][ii]->WireID().Plane<<" w "<<trkHits[ipl][ii]->WireID().Wire<<" t "<<(int)trkHits[ipl][ii]->PeakTime();
3184  } // ipl
3185 
3186  // TODO Check the ends of trkHits to see if there are missing hits that should have been included
3187  // in a cluster
3188 
3189  } // FillTrkHits
3190 
3192  void CCTrackMaker::PrintTracks() const
3193  {
3194  mf::LogVerbatim myprt("CCTM");
3195  myprt<<"********* PrintTracks \n";
3196  myprt<<"vtx Index X Y Z\n";
3197  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
3198  myprt<<std::right<<std::setw(4)<<ivx<<std::setw(4)<<vtx[ivx].EvtIndex;
3199  myprt<<std::fixed;
3200  myprt<<std::right<<std::setw(10)<<std::setprecision(1)<<vtx[ivx].X;
3201  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<vtx[ivx].Y;
3202  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<vtx[ivx].Z;
3203  if(vtx[ivx].Neutrino) myprt<<" Neutrino vertex";
3204  myprt<<"\n";
3205  } // ivx
3206 
3207  myprt<<">>>>>>>>>> Tracks \n";
3208  myprt<<"trk ID Proc nht nTrj sX sY sZ eX eY eZ sVx eVx sGd eGd ChgOrd dirZ Mom PDG ClsIndices\n";
3209  for(unsigned short itr = 0; itr < trk.size(); ++itr) {
3210  myprt<<std::right<<std::setw(3)<<itr<<std::setw(4)<<trk[itr].ID;
3211  myprt<<std::right<<std::setw(5)<<std::setw(4)<<trk[itr].Proc;
3212  unsigned short nht = 0;
3213  for(unsigned short ii = 0; ii < 3; ++ii) nht += trk[itr].TrkHits[ii].size();
3214  myprt<<std::right<<std::setw(5)<<nht;
3215  myprt<<std::setw(4)<<trk[itr].TrjPos.size();
3216  myprt<<std::fixed;
3217  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<trk[itr].TrjPos[0](0);
3218  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<trk[itr].TrjPos[0](1);
3219  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<trk[itr].TrjPos[0](2);
3220  unsigned short itj = trk[itr].TrjPos.size() - 1;
3221  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<trk[itr].TrjPos[itj](0);
3222  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<trk[itr].TrjPos[itj](1);
3223  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<trk[itr].TrjPos[itj](2);
3224  myprt<<std::setw(4)<<trk[itr].VtxIndex[0]<<std::setw(4)<<trk[itr].VtxIndex[1];
3225  myprt<<std::setw(4)<<trk[itr].GoodEnd[0];
3226  myprt<<std::setw(4)<<trk[itr].GoodEnd[1];
3227  myprt<<std::setw(4)<<trk[itr].ChgOrder;
3228  myprt<<std::right<<std::setw(10)<<std::setprecision(3)<<trk[itr].TrjDir[itj](2);
3229  myprt<<std::right<<std::setw(4)<<trk[itr].MomID;
3230  myprt<<std::right<<std::setw(5)<<trk[itr].PDGCode<<" ";
3231  for(unsigned short ii = 0; ii < trk[itr].ClsEvtIndices.size(); ++ii) myprt<<" "<<trk[itr].ClsEvtIndices[ii];
3232  myprt<<"\n";
3233  } // itr
3234 
3235  } // PrintTracks
3236 
3237 
3240  {
3241 
3242  unsigned short iTime;
3243  mf::LogVerbatim myprt("CCTM");
3244  myprt<<"******* PrintClusters ********* Num_Clusters_in Wire:Time\n";
3245  myprt<<"vtx Index X Y Z Pln0 Pln1 Pln2 Pln0 Pln1 Pln2\n";
3246  for(unsigned short ivx = 0; ivx < vtx.size(); ++ivx) {
3247  myprt<<std::right<<std::setw(3)<<ivx<<std::setw(7)<<ivx;
3248  myprt<<std::fixed;
3249  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<vtx[ivx].X;
3250  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<vtx[ivx].Y;
3251  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<vtx[ivx].Z;
3252  myprt<<std::right<<std::setw(5)<<vtx[ivx].nClusInPln[0];
3253  myprt<<std::right<<std::setw(5)<<vtx[ivx].nClusInPln[1];
3254  myprt<<std::right<<std::setw(5)<<vtx[ivx].nClusInPln[2];
3255  myprt<<" ";
3256  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
3257  int time = (0.5 + detprop->ConvertXToTicks(vtx[ivx].X, ipl, tpc, cstat));
3258  int wire = geom->WireCoordinate(vtx[ivx].Y, vtx[ivx].Z, ipl, tpc, cstat);
3259  myprt<<std::right<<std::setw(7)<<wire<<":"<<time;
3260  }
3261 
3262  myprt<<"\n";
3263  } // ivx
3264 
3265  for(unsigned short ipl = 0; ipl < nplanes; ++ipl) {
3266  myprt<<">>>>>>>>>> Cluster chains in Plane "<<ipl<<"\n";
3267  myprt<<"ipl ccl Len Chg W0:T0 Ang0 Dir0 Vx0 mBk0 W1:T1 Ang1 Dir1 Vx1 mBk1 InTk cls:Order \n";
3268  for(unsigned short ccl = 0; ccl < clsChain[ipl].size(); ++ccl) {
3269  myprt<<std::right<<std::setw(3)<<ipl;
3270  myprt<<std::right<<std::setw(5)<<ccl;
3271  myprt<<std::right<<std::setw(6)<<clsChain[ipl][ccl].Length;
3272  myprt<<std::right<<std::setw(8)<<(int)clsChain[ipl][ccl].TotChg;
3273  for(unsigned short end = 0; end < 2; ++end) {
3274  iTime = clsChain[ipl][ccl].Time[end];
3275  myprt<<std::right<<std::setw(5)<<(int)clsChain[ipl][ccl].Wire[end]
3276  <<":"<<std::setprecision(1)<<iTime;
3277  if(iTime < 10) {
3278  myprt<<" ";
3279  } else if(iTime < 100) {
3280  myprt<<" ";
3281  } else if(iTime < 1000) myprt<<" ";
3282  myprt<<std::right<<std::setw(7)<<std::setprecision(2)<<clsChain[ipl][ccl].Angle[end];
3283  myprt<<std::right<<std::setw(5)<<clsChain[ipl][ccl].Dir[end];
3284  myprt<<std::right<<std::setw(5)<<clsChain[ipl][ccl].VtxIndex[end];
3285  myprt<<std::fixed<<std::right<<std::setw(6)<<std::setprecision(1)<<clsChain[ipl][ccl].mBrkIndex[end];
3286 // myprt<<std::fixed<<std::right<<std::setw(6)<<std::setprecision(1)<<clsChain[ipl][ccl].ChgNear[end];
3287  }
3288  myprt<<std::right<<std::setw(7)<<clsChain[ipl][ccl].InTrack;
3289  myprt<<" ";
3290  for(unsigned short ii = 0; ii < clsChain[ipl][ccl].ClsIndex.size(); ++ii)
3291  myprt<<" "<<clsChain[ipl][ccl].ClsIndex[ii]<<":"<<clsChain[ipl][ccl].Order[ii];
3292  myprt<<"\n";
3293  } // ccl
3294  if(fPrintAllClusters) {
3295  myprt<<">>>>>>>>>> Clusters in Plane "<<ipl<<"\n";
3296  myprt<<"ipl icl Evt Len Chg W0:T0 Ang0 Dir0 Vx0 CN0 W1:T1 Ang1 Dir1 Vx1 CN1 InTk Brk0 MrgEr0 Brk1 MrgEr1\n";
3297  for(unsigned short icl = 0; icl < cls[ipl].size(); ++icl) {
3298  myprt<<std::right<<std::setw(3)<<ipl;
3299  myprt<<std::right<<std::setw(5)<<icl;
3300  myprt<<std::right<<std::setw(5)<<cls[ipl][icl].EvtIndex;
3301  myprt<<std::right<<std::setw(6)<<cls[ipl][icl].Length;
3302  myprt<<std::right<<std::setw(8)<<(int)cls[ipl][icl].TotChg;
3303  for(unsigned short end = 0; end < 2; ++end) {
3304  iTime = cls[ipl][icl].Time[end];
3305  myprt<<std::right<<std::setw(5)<<(int)cls[ipl][icl].Wire[end]<<":"<<iTime;
3306  if(iTime < 10) {
3307  myprt<<" ";
3308  } else if(iTime < 100) {
3309  myprt<<" ";
3310  } else if(iTime < 1000) myprt<<" ";
3311  myprt<<std::right<<std::setw(7)<<std::setprecision(2)<<cls[ipl][icl].Angle[end];
3312  myprt<<std::right<<std::setw(5)<<cls[ipl][icl].Dir[end];
3313  myprt<<std::right<<std::setw(5)<<cls[ipl][icl].VtxIndex[end];
3314  myprt<<std::fixed<<std::right<<std::setw(5)<<std::setprecision(1)<<cls[ipl][icl].ChgNear[end];
3315  }
3316  myprt<<std::fixed;
3317  myprt<<std::right<<std::setw(5)<<cls[ipl][icl].InTrack;
3318  myprt<<std::right<<std::setw(5)<<(int)cls[ipl][icl].BrkIndex[0];
3319  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<cls[ipl][icl].MergeError[0];
3320  myprt<<std::right<<std::setw(5)<<(int)cls[ipl][icl].BrkIndex[1];
3321  myprt<<std::right<<std::setw(7)<<std::setprecision(1)<<cls[ipl][icl].MergeError[1];
3322  myprt<<"\n";
3323  } // icl
3324  } // fPrintAllClusters
3325  } // ipl
3326  } // PrintClusters
3327 
3329  float CCTrackMaker::AngleFactor(float slope)
3330  {
3331  float slp = fabs(slope);
3332  if(slp > 10.) slp = 30.;
3333  // return a value between 1 and 46
3334  return 1 + 0.05 * slp * slp;
3335  } // AngleFactor
3336 
3338  float CCTrackMaker::ChargeNear(unsigned short ipl, unsigned short wire1, float time1, unsigned short wire2, float time2)
3339  {
3340  // returns the hit charge along a line between (wire1, time1) and
3341  // (wire2, time2)
3342 
3343  // put in increasing wire order (wire2 > wire1)
3344  unsigned short w1 = wire1;
3345  unsigned short w2 = wire2;
3346  double t1 = time1;
3347  double t2 = time2;
3348  double slp, prtime;
3349  if(w1 == w2) {
3350  slp = 0;
3351  } else {
3352  if(w1 > w2) {
3353  w1 = wire2;
3354  w2 = wire1;
3355  t1 = time2;
3356  t2 = time1;
3357  }
3358  slp = (t2 - t1) / (w2 - w1);
3359  }
3360 
3361  unsigned short wire;
3362 
3363  float chg = 0;
3364  for(unsigned short hit = 0; hit < allhits.size(); ++hit) {
3365  if(allhits[hit]->WireID().Cryostat != cstat) continue;
3366  if(allhits[hit]->WireID().TPC != tpc) continue;
3367  if(allhits[hit]->WireID().Plane != ipl) continue;
3368  wire = allhits[hit]->WireID().Wire;
3369  if(wire < w1) continue;
3370  if(wire > w2) continue;
3371  prtime = t1 + (wire - w1) * slp;
3372  // std::cout<<"prtime "<<wire<<":"<<(int)prtime<<" hit "<<allhits[hit]->PeakTimeMinusRMS(3)<<" "<<allhits[hit]->PeakTimePlusRMS(3)<<"\n";
3373  if(prtime > allhits[hit]->PeakTimePlusRMS(3)) continue;
3374  if(prtime < allhits[hit]->PeakTimeMinusRMS(3)) continue;
3375  chg += ChgNorm[ipl] * allhits[hit]->Integral();
3376  } // hit
3377  return chg;
3378  } // ChargeNear
3379 
3382  {
3383  // fills the WireHitRange vector. Slightly modified version of the one in ClusterCrawlerAlg
3384 
3385  unsigned short ipl;
3386 
3387  // initialize everything
3388  for(ipl = 0; ipl < 3; ++ipl) {
3389  firstWire[ipl] = INT_MAX;
3390  lastWire[ipl] = 0;
3391  firstHit[ipl] = INT_MAX;
3392  lastHit[ipl] = 0;
3393  WireHitRange[ipl].clear();
3394  ChgNorm[ipl] = 0;
3395  }
3396 
3397  // find the first and last wire with a hit
3398  unsigned short oldipl = 0;
3399  for(unsigned int hit = 0; hit < allhits.size(); ++hit) {
3400  if(allhits[hit]->WireID().Cryostat != cstat) continue;
3401  if(allhits[hit]->WireID().TPC != tpc) continue;
3402  ipl = allhits[hit]->WireID().Plane;
3403  if(allhits[hit]->WireID().Wire > geom->Nwires(ipl, tpc, cstat)) {
3404  if(lastWire[ipl] < firstWire[ipl]) {
3405  mf::LogError("CCTM")<<"Invalid WireID().Wire "<<allhits[hit]->WireID().Wire;
3406  return;
3407  }
3408  }
3409 // ChgNorm[ipl] += allhits[hit]->Integral();
3410  if(ipl < oldipl) {
3411  mf::LogError("CCTM")<<"Hits are not in increasing-plane order\n";
3412  return;
3413  }
3414  oldipl = ipl;
3415  if(firstHit[ipl] == INT_MAX) {
3416  firstHit[ipl] = hit;
3417  firstWire[ipl] = allhits[hit]->WireID().Wire;
3418  }
3419  lastHit[ipl] = hit;
3420  lastWire[ipl] = allhits[hit]->WireID().Wire;
3421  } // hit
3422 
3423  // xxx
3424  for(ipl = 0; ipl < nplanes; ++ipl) {
3425  if(lastWire[ipl] < firstWire[ipl]) {
3426  mf::LogError("CCTM")<<"Invalid first/last wire in plane "<<ipl;
3427  return;
3428  }
3429  } // ipl
3430 
3431  // normalize charge in induction planes to the collection plane
3432 // for(ipl = 0; ipl < nplanes; ++ipl) ChgNorm[ipl] = ChgNorm[nplanes - 1] / ChgNorm[ipl];
3433  for(ipl = 0; ipl < nplanes; ++ipl) ChgNorm[ipl] = 1;
3434 
3435  // get the service to learn about channel status
3436  //lariov::ChannelStatusProvider const& channelStatus
3437  // = art::ServiceHandle<lariov::ChannelStatusService>()->GetProvider();
3438 
3439  // now we can define the WireHitRange vector.
3440  int sflag, nwires, wire;
3441  unsigned int indx, thisWire, thisHit, lastFirstHit;
3442  //uint32_t chan;
3443  for(ipl = 0; ipl < nplanes; ++ipl) {
3444  nwires = lastWire[ipl] - firstWire[ipl] + 1;
3445  WireHitRange[ipl].resize(nwires);
3446  // start by defining the "no hits on wire" condition
3447  sflag = -2;
3448  for(wire = 0; wire < nwires; ++wire) WireHitRange[ipl][wire] = std::make_pair(sflag, sflag);
3449  // overwrite with the "dead wires" condition
3450  sflag = -1;
3451  for(wire = 0; wire < nwires; ++wire) {
3452  //chan = geom->PlaneWireToChannel(ipl, wire, tpc, cstat);
3453  //if(channelStatus.IsBad(chan)) {
3454  // indx = wire - firstWire[ipl];
3455  // WireHitRange[ipl][indx] = std::make_pair(sflag, sflag);
3456  //}
3457  } // wire
3458  // next overwrite with the index of the first/last hit on each wire
3459  lastWire[ipl] = firstWire[ipl];
3460  thisHit = firstHit[ipl];
3461  lastFirstHit = firstHit[ipl];
3462  for(unsigned int hit = firstHit[ipl]; hit <= lastHit[ipl]; ++hit) {
3463  thisWire = allhits[hit]->WireID().Wire;
3464  if(thisWire > lastWire[ipl]) {
3465  indx = lastWire[ipl] - firstWire[ipl];
3466  int tmp1 = lastFirstHit;
3467  int tmp2 = thisHit;
3468  WireHitRange[ipl][indx] = std::make_pair(tmp1, tmp2);
3469  lastWire[ipl] = thisWire;
3470  lastFirstHit = thisHit;
3471  } else if(thisWire < lastWire[ipl]) {
3472  mf::LogError("CCTM")<<"Hit not in proper order in plane "<<ipl;
3473  exit(1);
3474  }
3475  ++thisHit;
3476  } // hit
3477  // define the last wire
3478  indx = lastWire[ipl] - firstWire[ipl];
3479  int tmp1 = lastFirstHit;
3480  ++lastHit[ipl];
3481  int tmp2 = lastHit[ipl];
3482  WireHitRange[ipl][indx] = std::make_pair(tmp1, tmp2);
3483  // add one to lastWire and lastHit for more standard indexing
3484  ++lastWire[ipl];
3485  } // ipl
3486 
3487  // error checking
3488  for(ipl = 0; ipl < nplanes; ++ipl) {
3489  if(firstWire[ipl] < INT_MAX) continue;
3490  if(lastWire[ipl] > 0) continue;
3491  if(firstHit[ipl] < INT_MAX) continue;
3492  if(lastHit[ipl] > 0) continue;
3493  std::cout<<"FWHR problem\n";
3494  exit(1);
3495  } // ipl
3496 
3497  unsigned int nht = 0;
3498  std::vector<bool> hchk(allhits.size());
3499  for(unsigned int ii = 0; ii < hchk.size(); ++ii) hchk[ii] = false;
3500  for(ipl = 0; ipl < nplanes; ++ipl) {
3501  for(unsigned int w = firstWire[ipl]; w < lastWire[ipl]; ++w) {
3502  indx = w - firstWire[ipl];
3503  if(indx > lastWire[ipl]) {
3504  std::cout<<"FWHR bad index "<<indx<<"\n";
3505  exit(1);
3506  }
3507  // no hit on this wire
3508  if(WireHitRange[ipl][indx].first < 0) continue;
3509  unsigned int firhit = WireHitRange[ipl][indx].first;
3510  unsigned int lashit = WireHitRange[ipl][indx].second;
3511  for(unsigned int hit = firhit; hit < lashit; ++hit) {
3512  ++nht;
3513  if(hit > allhits.size() -1) {
3514  std::cout<<"FWHR: Bad hchk "<<hit<<" size "<<allhits.size()<<"\n";
3515  continue;
3516  }
3517  hchk[hit] = true;
3518  if(allhits[hit]->WireID().Plane != ipl || allhits[hit]->WireID().Wire != w) {
3519  std::cout<<"FWHR bad plane "<<allhits[hit]->WireID().Plane<<" "<<ipl<<" or wire "<<allhits[hit]->WireID().Wire<<" "<<w<<"\n";
3520  exit(1);
3521  }
3522  } // hit
3523  } // w
3524  } // ipl
3525 
3526  if(nht != allhits.size()) {
3527  std::cout<<"FWHR hit count problem "<<nht<<" "<<allhits.size()<<"\n";
3528  for(unsigned int ii = 0; ii < hchk.size(); ++ii) if(!hchk[ii]) std::cout<<" "<<ii<<" "<<allhits[ii]->WireID().Plane<<" "<<allhits[ii]->WireID().Wire<<" "<<(int)allhits[ii]->PeakTime()<<"\n";
3529  exit(1);
3530  }
3531 
3532  } // FillWireHitRange
3534 
3535 } // namespace
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
std::vector< MatchPars > matcomb
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
std::array< std::vector< art::Ptr< recob::Hit > >, 3 > TrkHits
TTree * t1
Definition: plottest35.C:26
std::string fVertexModuleLabel
std::vector< float > fChgAsymFactor
std::array< unsigned short, 3 > End
Declaration of signal hit object.
void PrintClusters()
std::array< unsigned int, 3 > firstWire
Float_t Y
Definition: plot.C:39
Planes which measure X direction.
Definition: geo_types.h:81
Geometry information for a single TPC.
Definition: TPCGeo.h:37
std::vector< art::Ptr< recob::Hit > > allhits
std::array< short, 2 > Dir
float StartWire() const
Returns the wire coordinate of the start of the cluster.
Definition: Cluster.h:286
Plane(const Point_t &planePos, const Vector_t &planeDir)
Constructor from reference position on the plane and direction orthogonal to the plane.
Definition: TrackingPlane.h:61
std::string fClusterModuleLabel
TrackTrajectoryAlg fTrackTrajectoryAlg
Set of hits with a 2D structure.
Definition: Cluster.h:71
std::array< short, 2 > BrkIndex
float EndTick() const
Returns the tick coordinate of the end of the cluster.
Definition: Cluster.h:342
Float_t tmp
Definition: plot.C:37
std::vector< unsigned short > pfpToTrkID
data_const_reference data(size_type i) const
Definition: FindManyP.h:458
std::vector< TVector3 > TrjPos
std::vector< TrkPar > trk
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
Cluster finding and building.
std::array< float, 3 > ChgNorm
std::vector< float > fAngleMatchErr
std::array< float, 2 > ChgNear
std::array< std::vector< clPar >, 3 > cls
float StartAngle() const
Returns the starting angle of the cluster.
Definition: Cluster.h:475
Definition of vertex object for LArSoft.
Definition: Vertex.h:35
std::array< float, 2 > Slope
std::array< bool, 2 > EndInTPC
std::array< std::vector< art::Ptr< recob::Hit > >, 3 > seedHits
Float_t Z
Definition: plot.C:39
ProductID put(std::unique_ptr< PROD > &&product)
Definition: Event.h:102
std::array< std::vector< ClsChainPar >, 3 > clsChain
float EndCharge() const
Returns the charge on the last wire of the cluster.
Definition: Cluster.h:498
Int_t max
Definition: plot.C:27
TCanvas * c1
Definition: plotHisto.C:7
TCanvas * c2
Definition: plot_hist.C:75
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:42
void beginJob()
Definition: Breakpoints.cc:14
long seed
Definition: chem4.cc:68
std::array< std::vector< std::pair< int, int > >, 3 > WireHitRange
std::vector< float > fMatchMinLen
T get(std::string const &key) const
Definition: ParameterSet.h:231
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
std::vector< unsigned short > Order
std::array< short, 2 > VtxIndex
std::array< short, 2 > mVtxIndex
std::vector< vtxPar > vtx
std::array< float, 2 > MergeError
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
std::array< bool, 2 > GoodEnd
bool CreateAssn(PRODUCER const &prod, art::Event &evt, std::vector< T > const &a, art::Ptr< U > const &b, art::Assns< U, T > &assn, std::string a_instance, size_t indx=UINT_MAX)
Creates a single one-to-one association.
Declaration of cluster object.
Provides recob::Track data product.
std::array< unsigned int, 3 > firstHit
std::array< std::vector< art::Ptr< recob::Hit > >, 3 > trkHits
std::array< float, 2 > Charge
std::vector< unsigned short > ClsIndex
std::vector< unsigned short > ClsEvtIndices
Detector simulation of raw signals on wires.
std::array< unsigned int, 3 > lastWire
TTree * t2
Definition: plottest35.C:36
Class defining a plane for tracking. It provides various functionalities to convert track parameters ...
Definition: TrackingPlane.h:37
Encapsulate the geometry of a wire.
bool greaterThan(CluLen c1, CluLen c2)
Hierarchical representation of particle flow.
Definition: PFParticle.h:44
Utility object to perform functions of association.
bool FillWireHitRange(TjStuff &tjs, const geo::TPCID &tpcid)
Definition: Utils.cxx:3722
Float_t norm
Encapsulate the construction of a single detector plane.
float StartCharge() const
Returns the charge on the first wire of the cluster.
Definition: Cluster.h:454
TDirectory * dir
Definition: macro.C:5
bool getByLabel(std::string const &label, std::string const &productInstanceName, Handle< PROD > &result) const
Definition: DataViewImpl.h:344
std::array< short, 2 > VtxIndex
void clear()
Definition: Handle.h:237
std::array< unsigned int, 3 > lastHit
std::array< float, 2 > X
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
art::ServiceHandle< geo::Geometry > geom
bool lessThan(CluLen c1, CluLen c2)
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
constexpr double kBogusD
obviously bogus double value
std::vector< TVector3 > TrjDir
std::vector< bool > fMakeAlgTracks
std::vector< short > fMatchAlgs
std::vector< float > fXMatchErr
void fill_ptr_vector(std::vector< Ptr< T >> &ptrs, H const &h)
Definition: Ptr.h:464
float EndAngle() const
Returns the ending angle of the cluster.
Definition: Cluster.h:519
recob::tracking::Plane Plane
Definition: TrackState.h:17
Float_t X
Definition: plot.C:39
Float_t track
Definition: plot.C:34
std::array< float, 2 > Wire
Float_t w
Definition: plot.C:23
float StartTick() const
Returns the tick coordinate of the start of the cluster.
Definition: Cluster.h:297
void LocalToWorld(const double *tpc, double *world) const
Transform point from local TPC frame to world frame.
Definition: TPCGeo.h:490
std::array< short, 2 > Time
art framework interface to geometry description
Track from a non-cascading particle.A recob::Track consists of a recob::TrackTrajectory, plus additional members relevant for a "fitted" track:
Definition: Track.h:51
std::array< float, 2 > Angle
vec_iX clear()
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
const detinfo::DetectorProperties * detprop
Encapsulate the construction of a single detector plane.
std::array< std::vector< unsigned short >, 3 > vxCls
std::array< unsigned short, 3 > nClusInPln
unsigned short fNVtxTrkHitsFit
float Integral() const
Returns the total charge of the cluster from hit shape.
Definition: Cluster.h:600
float EndWire() const
Returns the wire coordinate of the end of the cluster.
Definition: Cluster.h:329
vertex reconstruction