tpccathodestitch_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 // Class: tpccathodestitch
3 // Plugin Type: producer (art v3_00_00)
4 // File: tpccathodestitch_module.cc
5 //
6 // Generated at Thu Nov 21 15:00:00 2019 by Thomas Junk modified from tpctrackfit2_module.cc
7 // Finds pairs of tracks on either side of the cathode plane and merges them.
8 // Inputs -- tracks fit by the track fitter, and associations with TPCClusters
9 // Outputs -- a completely new set of tracks, trackioniz, and associations of tracks with TPCClusters and trackioniz.
10 // Also a new set of vertices that are now moved because we have new locations for the tracks.
11 // No change for tracks that have not been stitched, just copies. Tracks that have been stitched correspond to fewer tracks on output
12 // also fills in a time for the stitched track. Because the magnetic field is nonuniform, tracks ought to be re-fit.
13 //
14 // We shift the vertices associated with shifted tracks, and also the tracks associated with those vertices. We look up those
15 // vertices by their associations with the tracks, and make new collections of everything: tracks, vertices, trkIoniz, and associations
16 // thereof. Also make new track-TPCCluster associations. We assume that all the vertices, including the ones not to be shifted,
17 // are associated with at least one track in the input track collection. We also shift associated TPCClusters and make a new
18 // collection of them which can be used in a subsequent track refit
19 ////////////////////////////////////////////////////////////////////////
20 
28 #include "fhiclcpp/ParameterSet.h"
31 #include "canvas/Persistency/Common/FindManyP.h"
32 
33 #include <memory>
34 #include <map>
35 
36 // ROOT includes
37 
38 #include "TMath.h"
39 #include "TVector3.h"
40 
41 // GArSoft Includes
49 #include "Reco/TrackPar.h"
50 #include "Reco/tracker2algs.h"
52 #include "Geometry/GeometryGAr.h"
53 
54 #include "Geant4/G4ThreeVector.hh"
55 
56 // #include "nug4/MagneticFieldServices/MagneticFieldService.h"
57 
58 namespace gar {
59  namespace rec {
60 
62  public:
63  explicit tpccathodestitch(fhicl::ParameterSet const& p);
64  // The compiler-generated destructor is fine for non-base
65  // classes without bare pointers or other resource use.
66 
67  // Plugins should not be copied or assigned.
68  tpccathodestitch(tpccathodestitch const&) = delete;
72 
73  // Required functions.
74  void produce(art::Event& e) override;
75 
76  private:
77 
78  // Declare member data here.
79 
80  std::string fInputTrackLabel; ///< input tracks and associations with TPCClusters. Get vertices associated with these tracks from the assns
81  std::string fInputVertexLabel; ///< input vertices and associations with tracks
82  std::string fInputTPCClusterLabel; ///< input TPC Clusters
83  std::string fInputHitLabel; ///< needed to make TPCCluster - hit associations
84  int fPrintLevel; ///< debug printout: 0: none, 1: just track parameters and residuals, 2: all
85  float fDistCut; ///< cut in distance between best-matching points
86  float fCTCut; ///< cut on cosine of angle matching
87  float fMaxDX; ///< cut on maximum translation of dX on one side.
88  float fMinDX; ///< cut on minimum translation of dX on one side.
89 
90  // returns true if trackA and trackB look like they were split across the cathode and need merging
91  // fwdflag variables say whether the beginning endpoint is the one on the stitched end (1) or the endpoint
92  // is the one on the stitched end (2). Both flags are set to zero if the track is not stitchable
93  // deltaX is the shift in X needed to be applied to track A, and -deltaX is to be applied to track B.
94 
95  bool cathodematch(const gar::rec::Track &trackA, const gar::rec::Track &trackB, int &fwdflagA, int &fwdflagB, float &deltaX);
96 
97  };
98 
99 
101  {
102  fInputTrackLabel = p.get<std::string>("InputTrackLabel","track");
103  fInputVertexLabel = p.get<std::string>("InputVertexLabel","vertex");
104  fInputTPCClusterLabel = p.get<std::string>("InputTPCClusterLabel","tpccluster");
105  fInputHitLabel = p.get<std::string>("InputHitLabel","hit");
106  fPrintLevel = p.get<int>("PrintLevel",0);
107  fDistCut = p.get<float>("DistCut",3);
108  fCTCut = p.get<float>("CTCut",0.99);
109  fMaxDX = p.get<float>("MaxDX",50.0);
110  fMinDX = p.get<float>("MinDX",-50.0);
111 
112  art::InputTag inputTrackTag(fInputTrackLabel);
113  consumes< std::vector<rec::Track> >(inputTrackTag);
114  consumes< art::Assns<rec::TPCCluster, rec::Track> >(inputTrackTag);
115  art::InputTag inputVertexTag(fInputVertexLabel);
116  consumes< art::Assns<rec::Vertex, rec::Track> >(inputVertexTag);
117  consumes<std::vector<rec::TrackIoniz>>(inputTrackTag);
118  consumes<art::Assns<rec::TrackIoniz, rec::Track>>(inputTrackTag);
119  art::InputTag inputHitTag(fInputHitLabel);
120  consumes<art::Assns<rec::Hit, rec::TPCCluster>>(inputHitTag);
121  art::InputTag inputTPCClusterTag(fInputTPCClusterLabel);
122  consumes< std::vector<rec::TPCCluster>>(inputTPCClusterTag);
123 
124  // probably don't need the vector hits at this point if we have the TPCClusters
125  //consumes< std::vector<rec::VecHit> >(patrecTag);
126  //consumes< art::Assns<rec::VecHit, rec::Track> >(patrecTag);
127 
128  produces< std::vector<rec::Track> >();
129  produces< art::Assns<rec::TPCCluster, rec::Track> >();
130  produces<std::vector<rec::TrackIoniz>>();
131  produces<art::Assns<rec::TrackIoniz, rec::Track>>();
132  produces< std::vector<rec::Vertex> >();
133  produces< art::Assns<rec::Track, rec::Vertex, rec::TrackEnd> >();
134  produces< std::vector<gar::rec::TPCCluster> >();
135  produces< art::Assns<gar::rec::Hit, gar::rec::TPCCluster> >();
136  }
137 
138 
139 
141  {
142 
143  // some useful structs
144 
145  typedef struct locVtx
146  {
147  float fPosition[3];
148  float fCovMat[9];
149  double fTime;
150  } locVtx_t;
151 
152  // for keeping a list of track endpoints associated with vertices. Keep dx in here so we can average
153  // it at a later step
154 
155  typedef struct trkiend
156  {
157  size_t trkindex;
158  rec::TrackEnd trkend;
159  float dx;
160  } trkiend_t;
161 
163  float xtpccent = geo->TPCXCent();
164 
165  // get the distance corresponding to one ADC tick so we can report the time in ticks
166 
167  auto detProp = gar::providerFrom<detinfo::DetectorPropertiesService>();
168  double DriftVelocity = detProp->DriftVelocity(detProp->Efield(),detProp->Temperature()); // in cm per microsecond
169  //auto clockService = gar::providerFrom<detinfo::DetectorClocksServiceGAr>();
170  //double distonetick = DriftVelocity * (clockService->TPCTick2Time(1) - clockService->TPCTick2Time(0)) ;
171 
172  // output collections
173 
174  std::unique_ptr< std::vector<rec::Track> > trkCol(new std::vector<rec::Track>);
175  std::unique_ptr<std::vector<gar::rec::TPCCluster> > TPCClusterCol (new std::vector<gar::rec::TPCCluster> );
176  std::unique_ptr< art::Assns<rec::TPCCluster,rec::Track> > TPCClusterTrkAssns(new art::Assns<rec::TPCCluster,rec::Track>);
177  std::unique_ptr< std::vector<rec::TrackIoniz> > ionCol(new std::vector<rec::TrackIoniz>);
178  std::unique_ptr< art::Assns<rec::TrackIoniz,rec::Track> > ionTrkAssns(new art::Assns<rec::TrackIoniz,rec::Track>);
179  std::unique_ptr< std::vector<rec::Vertex> > vtxCol(new std::vector<rec::Vertex>);
180  std::unique_ptr< art::Assns<rec::Track, rec::Vertex, rec::TrackEnd> > trkVtxAssns(new art::Assns<rec::Track, rec::Vertex, rec::TrackEnd>);
181  std::unique_ptr< art::Assns<rec::Hit,rec::TPCCluster> > HitTPCClusterAssns(new art::Assns<rec::Hit,rec::TPCCluster>);
182 
183  // inputs
184 
185  auto inputTrackHandle = e.getValidHandle< std::vector<rec::Track> >(fInputTrackLabel);
186  auto const& inputTracks = *inputTrackHandle;
187  auto inputTPCClusterHandle = e.getValidHandle< std::vector<rec::TPCCluster> >(fInputTPCClusterLabel);
188  auto const& inputTPCClusters = *inputTPCClusterHandle;
189 
190  const art::FindManyP<rec::TPCCluster> TPCClustersFromInputTracks(inputTrackHandle,e,fInputTrackLabel);
191  const art::FindManyP<rec::TrackIoniz> TrackIonizFromInputTracks(inputTrackHandle,e,fInputTrackLabel);
192  const art::FindManyP<rec::Vertex, rec::TrackEnd> VerticesFromInputTracks(inputTrackHandle,e,fInputVertexLabel);
193  const art::FindManyP<rec::Hit> HitsFromInputTPCClusters(inputTPCClusterHandle,e,fInputTPCClusterLabel);
194 
195  // we get the input ionization data products from the associations
196  //auto inputIonHandle = e.getValidHandle< std::vector<rec::TrackIoniz> >(fInputTrackLabel);
197  //auto const& inputIoniz = *inputIonHandle;
198 
199  // for making output associations
200 
201  auto const trackPtrMaker = art::PtrMaker<rec::Track>(e);
202  auto const ionizPtrMaker = art::PtrMaker<rec::TrackIoniz>(e);
203  auto const TPCClusterPtrMaker = art::PtrMaker<rec::TPCCluster>(e);
204  auto const vtxPtrMaker = art::PtrMaker<rec::Vertex>(e);
205 
206 
207  // and a map of TPC Cluster ID numbers to locations in the hit-cluster assocation FindManyP
208 
209  std::map<rec::IDNumber,size_t> hcim;
210  for (size_t iclus = 0; iclus<inputTPCClusters.size(); ++iclus)
211  {
212  rec::IDNumber clusid = inputTPCClusters.at(iclus).getIDNumber();
213  hcim[clusid] = iclus;
214  }
215 
216  // make a list of which side each track is on by checking the majority of hits
217  // this is used so we do not stitch tracks on the same side of the cathode
218 
219  std::vector<int> trackside;
220  float chanpos[3] = {0,0,0};
221  for (size_t itrack = 0; itrack < inputTracks.size(); ++itrack)
222  {
223  size_t nplus = 0;
224  size_t nminus = 0;
225  for (size_t iTPCCluster=0; iTPCCluster<TPCClustersFromInputTracks.at(itrack).size(); ++iTPCCluster)
226  {
227  size_t icindex = hcim[TPCClustersFromInputTracks.at(itrack).at(iTPCCluster)->getIDNumber()];
228  for (size_t ihit=0; ihit < HitsFromInputTPCClusters.at(icindex).size(); ++ihit)
229  {
230  auto hitchan = HitsFromInputTPCClusters.at(icindex).at(ihit)->Channel();
231  geo->ChannelToPosition(hitchan, chanpos);
232  if (chanpos[0] > xtpccent)
233  {
234  nplus++;
235  }
236  else
237  {
238  nminus++;
239  }
240  }
241  }
242  if (nplus > nminus)
243  {
244  trackside.push_back(1);
245  }
246  else
247  {
248  trackside.push_back(-1);
249  }
250  }
251 
252  // vector of merged flags contains a list of which pairs of tracks are merged
253  // A more general track stitcher may want to stitch kinked tracks together and thus be able to stitch
254  // more than a pair of tracks, but this module so far just stitches across the cathode.
255 
256  size_t nti = inputTracks.size();
257  std::vector<int> mergedflag(nti,-1); // save indexes here; negative means unmerged
258  std::vector<int> fwdflag(nti,0); // 1 for forwards, 2 for backwards
259  std::vector<float> dx(nti,0); // shift in X needed
260 
261  for (size_t itrack = 0; itrack < inputTracks.size(); ++itrack)
262  {
263  if (mergedflag.at(itrack) >= 0) continue; // this track is already part of an earlier merge
264  for (size_t jtrack = itrack+1; jtrack < inputTracks.size(); ++jtrack)
265  {
266  if (mergedflag.at(jtrack)>=0) continue; // already merged, don't merge another one
267  if (trackside.at(itrack) == trackside.at(jtrack)) continue; // don't merge tracks on the same side
268  if (cathodematch(inputTracks.at(itrack),inputTracks.at(jtrack),fwdflag.at(itrack),fwdflag.at(jtrack),dx.at(itrack)))
269  {
270  if (fPrintLevel >0)
271  {
272  std::cout << "found a merge. dx= " <<
273  dx.at(itrack) <<
274  " flip flags: " << fwdflag.at(itrack) << " " << fwdflag.at(jtrack) << std::endl;
275  }
276  mergedflag.at(itrack) = jtrack;
277  mergedflag.at(jtrack) = itrack;
278  dx.at(jtrack) = -dx.at(itrack);
279  break;
280  }
281  }
282  }
283 
284  // we have lists of tracks to merge and how much to shift them by, and which ones to flip. Look for associated vertices
285  // and follow the chain of associated tracks, proposing new shifts to them.
286 
287  // make a local copy of vertex information so we can change the X locations
288 
289  std::map<rec::IDNumber,locVtx_t> vtxmap; // index of vertices by Leo's IDNumber
290 
291  // a map of vertex to track associations, the inverse of the track to vertex assocation list
292  std::map<rec::IDNumber,std::vector<trkiend_t>> vttrackmap;
293 
294  // keep track of which vertices we have already shifted
295  std::map<rec::IDNumber,bool> vtxshifted;
296 
297  // fill out the list of vertices. A track may belong to more than one vertex (either end, and a track end
298  // itself may belong to more than one vertex. fill in the dx's as needed.
299 
300  for (size_t itrack = 0; itrack < inputTracks.size(); ++itrack)
301  {
302  trkiend_t tmpti;
303  tmpti.trkindex = itrack;
304  tmpti.dx = dx.at(itrack);
305 
306  for (size_t ivtx = 0; ivtx < VerticesFromInputTracks.at(itrack).size(); ++ivtx)
307  {
308  auto vtxp = VerticesFromInputTracks.at(itrack).at(ivtx);
309  rec::IDNumber vtxid = vtxp->getIDNumber();
310  auto vtxiter = vtxmap.find(vtxid);
311  if (vtxiter == vtxmap.end())
312  {
313  locVtx_t tmpvtxdat;
314  tmpvtxdat.fTime = vtxp->Time();
315  for (int i=0; i<3; ++i)
316  {
317  tmpvtxdat.fPosition[i] = vtxp->Position()[i];
318  }
319  for (int i=0; i<9; ++i)
320  {
321  tmpvtxdat.fCovMat[i] = vtxp->CovMat()[i];
322  }
323  vtxmap[vtxid] = tmpvtxdat;
324  vtxshifted[vtxid] = false;
325 
326  std::vector<trkiend_t> trkindexvector;
327  tmpti.trkend = *(VerticesFromInputTracks.data(itrack).at(ivtx));
328  trkindexvector.push_back(tmpti);
329  vttrackmap[vtxid] = trkindexvector;
330  }
331  else
332  {
333  tmpti.trkend = *(VerticesFromInputTracks.data(itrack).at(ivtx));
334  vttrackmap[vtxid].push_back(tmpti);
335  }
336  }
337  }
338 
339  // Find new vertex shift positions by averaging the shifts of associated tracks that are on the
340  // cathode-crosser list. To do -- carry uncertainties around and include in the average.
341  // This while loop searches for chains of tracks and vertices to shift.
342 
343  bool moretodo = true;
344  while (moretodo)
345  {
346  moretodo = false;
347  for (const auto &vtxi : vttrackmap)
348  {
349  if (vtxshifted[vtxi.first]) continue; // skip the ones we've already shifted
350 
351  float avgdx = 0;
352  size_t numdx = 0;
353  for (size_t i=0; i< vtxi.second.size(); ++i)
354  {
355  size_t itrack = vtxi.second.at(i).trkindex;
356  if (mergedflag.at(itrack) >= 0 || dx.at(itrack) != 0)
357  {
358  avgdx += dx.at(itrack);
359  ++numdx;
360  }
361  }
362  if (numdx > 0)
363  {
364  avgdx /= (float) numdx;
365  vtxshifted[vtxi.first] = true; // we are shifting a vertex.
366  moretodo = true; // we'll shift its tracks and thus need to go around looking for more vertices
367  }
368  vtxmap[vtxi.first].fPosition[0] += avgdx;
369 
370  double ts = vtxmap[vtxi.first].fTime;
371  double deltat = avgdx/DriftVelocity;
372  ts += deltat;
373  vtxmap[vtxi.first].fTime = ts;
374 
375  // shift the as-yet-unshifted tracks (or rather mark them for shifting)
376  for (size_t i=0; i< vtxi.second.size(); ++i)
377  {
378  size_t itrack = vtxi.second.at(i).trkindex;
379  if (mergedflag.at(itrack) < 0 && dx.at(itrack) == 0)
380  {
381  dx.at(itrack) = avgdx;
382  }
383  }
384  }
385  }
386 
387  // put the new vertices into the output collection, and remember where we put them so we can
388  // use it in associations.
389 
390  std::map<rec::IDNumber,size_t> vtxoutmap;
391  for (const auto &vtxr : vtxmap)
392  {
393  vtxCol->emplace_back(vtxr.second.fPosition,vtxr.second.fCovMat,vtxr.second.fTime);
394  vtxoutmap[vtxr.first] = vtxCol->size() - 1;
395  }
396 
397 
398  // fill the output track collection, merging, shifting, and flipping as needed.
399  // fill in the associations, updating the
400  // trkend flags if the tracks have been flipped.
401 
402  for (size_t itrack = 0; itrack < inputTracks.size(); ++itrack)
403  {
404  if (mergedflag.at(itrack) < 0) // not merged, but make a new shifted track if need be
405  {
406  TrackPar tpi(inputTracks.at(itrack));
407  double ts = tpi.getTime();
408  double deltat = dx.at(itrack)/DriftVelocity;
409  ts += deltat;
410  TrackPar tpm(tpi.getLengthForwards(),
411  tpi.getLengthBackwards(),
412  tpi.getNTPCClusters(),
413  tpi.getXBeg() + dx.at(itrack),
414  tpi.getTrackParametersBegin(),
415  tpi.getCovMatBeg(),
416  tpi.getChisqForwards(),
417  tpi.getXEnd() + dx.at(itrack),
418  tpi.getTrackParametersEnd(),
419  tpi.getCovMatEnd(),
420  tpi.getChisqBackwards(),
421  ts);
422  trkCol->push_back(tpm.CreateTrack());
423 
424  //Make copies of the associated TPCClusters and shift them as need be.
425 
426  auto const trackpointer = trackPtrMaker(trkCol->size()-1);
427  float cpos[3] = {0,0,0};
428  for (size_t iTPCCluster=0; iTPCCluster<TPCClustersFromInputTracks.at(itrack).size(); ++iTPCCluster)
429  {
430  auto const &tc = TPCClustersFromInputTracks.at(itrack).at(iTPCCluster);
431  for (size_t i=0; i<3; ++i)
432  {
433  cpos[i] = tc->Position()[i];
434  }
435  cpos[0] += dx.at(itrack);
436  TPCClusterCol->emplace_back(tc->Signal(),
437  cpos,
438  tc->StartTime(),
439  tc->EndTime(),
440  tc->Time(),
441  tc->RMS(),
442  tc->CovMatPacked());
443  auto const TPCClusterPointer = TPCClusterPtrMaker(TPCClusterCol->size()-1);
444  TPCClusterTrkAssns->addSingle(TPCClusterPointer,trackpointer);
445  rec::IDNumber oldclusid = tc->getIDNumber();
446  size_t icindex = hcim[oldclusid];
447  for (size_t ihit=0; ihit < HitsFromInputTPCClusters.at(icindex).size(); ++ihit)
448  {
449  HitTPCClusterAssns->addSingle(HitsFromInputTPCClusters.at(icindex).at(ihit),TPCClusterPointer);
450  }
451  }
452 
453  // copy trkioniz and associations to the output collections.
454  // this loop may be unnecessary as there is only one TrackIoniz per Track, but just in case
455 
456  for (size_t iTrkIoniz=0; iTrkIoniz<TrackIonizFromInputTracks.at(itrack).size(); ++iTrkIoniz)
457  {
458  ionCol->push_back(*TrackIonizFromInputTracks.at(itrack).at(iTrkIoniz));
459  auto const ionizpointer = ionizPtrMaker(ionCol->size()-1);
460  ionTrkAssns->addSingle(ionizpointer, trackpointer);
461  }
462 
463  // copy over associations with vertices to the new vertex collection. Retain
464  // track-end identification as these tracks are not flipped.
465 
466  for (size_t ivtx = 0; ivtx < VerticesFromInputTracks.at(itrack).size(); ++ivtx)
467  {
468  auto vtxp = VerticesFromInputTracks.at(itrack).at(ivtx);
469  auto trkend = *(VerticesFromInputTracks.data(itrack).at(ivtx));
470  rec::IDNumber vtxid = vtxp->getIDNumber();
471  size_t ivout = vtxoutmap[vtxid];
472  auto const vtxptr = vtxPtrMaker(ivout);
473  trkVtxAssns->addSingle(trackpointer,vtxptr,trkend);
474  }
475  }
476  else // merge them -- adjust track parameters and put in the time
477  {
478  // assume directions are set so that itrack is the "beginning" and jtrack is the "end"
479  if (mergedflag.at(itrack) < 0)
480  {
481  MF_LOG_WARNING("tpccathodestitch: inconsistent merge flags ") << itrack << " " << mergedflag.at(itrack);
482  continue;
483  }
484  size_t jtrack = mergedflag.at(itrack);
485  if (jtrack < itrack) continue; // don't merge the same tracks twice
486 
487  // flip the tracks around according to the fwdflag returned by the cathode match method
488  // fwdflag == 1 means don't flip
489 
490  TrackPar tpi(inputTracks.at(itrack), fwdflag.at(itrack) != 1);
491  TrackPar tpj(inputTracks.at(jtrack), fwdflag.at(jtrack) != 1);
492 
493  tpi.setXBeg( tpi.getXBeg() + dx.at(itrack) );
494  tpi.setXEnd( tpi.getXEnd() + dx.at(itrack) );
495  tpj.setXBeg( tpj.getXBeg() + dx.at(jtrack) );
496  tpj.setXEnd( tpj.getXEnd() + dx.at(jtrack) );
497 
498  // some checking due to the unsigned nature of the timestmap. Assume in ticks.
499  double ts = tpi.getTime();
500  int deltat = dx.at(itrack)/DriftVelocity;
501  if ( (int) ts + deltat >= 0)
502  {
503  ts += deltat;
504  }
505 
506  TrackPar tpm(tpi.getLengthForwards() + tpj.getLengthForwards(),
507  tpi.getLengthBackwards() + tpj.getLengthBackwards(),
508  tpi.getNTPCClusters() + tpj.getNTPCClusters(),
509  tpi.getXBeg(),
510  tpi.getTrackParametersBegin(),
511  tpi.getCovMatBeg(),
512  tpi.getChisqForwards() + tpj.getChisqForwards(),
513  tpj.getXEnd(),
514  tpj.getTrackParametersEnd(),
515  tpj.getCovMatEnd(),
516  tpi.getChisqBackwards() + tpj.getChisqBackwards(),
517  ts);
518  trkCol->push_back(tpm.CreateTrack());
519  auto const trackpointer = trackPtrMaker(trkCol->size()-1);
520 
521  // make new TPCClusters, shifted with the track dx. To think about: the times. They are arrival times of hit charge at the moment
522 
523  float cpos[3] = {0,0,0};
524  for (size_t iTPCCluster=0; iTPCCluster<TPCClustersFromInputTracks.at(itrack).size(); ++iTPCCluster)
525  {
526  auto const &tc = TPCClustersFromInputTracks.at(itrack).at(iTPCCluster);
527  for (size_t i=0; i<3; ++i)
528  {
529  cpos[i] = tc->Position()[i];
530  }
531  cpos[0] += dx.at(itrack);
532  TPCClusterCol->emplace_back(tc->Signal(),
533  cpos,
534  tc->StartTime(),
535  tc->EndTime(),
536  tc->Time(),
537  tc->RMS(),
538  tc->CovMatPacked());
539  auto const TPCClusterPointer = TPCClusterPtrMaker(TPCClusterCol->size()-1);
540  TPCClusterTrkAssns->addSingle(TPCClusterPointer,trackpointer);
541  rec::IDNumber oldclusid = tc->getIDNumber();
542  size_t icindex = hcim[oldclusid];
543  for (size_t ihit=0; ihit < HitsFromInputTPCClusters.at(icindex).size(); ++ihit)
544  {
545  HitTPCClusterAssns->addSingle(HitsFromInputTPCClusters.at(icindex).at(ihit),TPCClusterPointer);
546  }
547  }
548  for (size_t iTPCCluster=0; iTPCCluster<TPCClustersFromInputTracks.at(jtrack).size(); ++iTPCCluster)
549  {
550  auto const &tc = TPCClustersFromInputTracks.at(jtrack).at(iTPCCluster);
551  for (size_t i=0; i<3; ++i)
552  {
553  cpos[i] = tc->Position()[i];
554  }
555  cpos[0] += dx.at(jtrack);
556  TPCClusterCol->emplace_back(tc->Signal(),
557  cpos,
558  tc->StartTime(),
559  tc->EndTime(),
560  tc->Time(),
561  tc->RMS(),
562  tc->CovMatPacked());
563  auto const TPCClusterPointer = TPCClusterPtrMaker(TPCClusterCol->size()-1);
564  TPCClusterTrkAssns->addSingle(TPCClusterPointer,trackpointer);
565  rec::IDNumber oldclusid = tc->getIDNumber();
566  size_t icindex = hcim[oldclusid];
567  for (size_t ihit=0; ihit < HitsFromInputTPCClusters.at(icindex).size(); ++ihit)
568  {
569  HitTPCClusterAssns->addSingle(HitsFromInputTPCClusters.at(icindex).at(ihit),TPCClusterPointer);
570  }
571  }
572 
573  // merge the track ionization data -- assume just one TrackIoniz per track, with forward and backward vectors
574  // reverse them if need be.
575 
576  const auto itif = TrackIonizFromInputTracks.at(itrack).at(0)->getFWD_dSigdXs();
577  const auto itib = TrackIonizFromInputTracks.at(itrack).at(0)->getBAK_dSigdXs();
578  const auto jtif = TrackIonizFromInputTracks.at(jtrack).at(0)->getFWD_dSigdXs();
579  const auto jtib = TrackIonizFromInputTracks.at(jtrack).at(0)->getBAK_dSigdXs();
580 
581  auto mtif = itif;
582  auto mtib = itib;
583  if (fwdflag.at(itrack) != 1) // flip itrack's ionization vector if need be
584  {
585  mtif = itib;
586  mtib = itif;
587  }
588  auto jmtif = jtif;
589  auto jmtib = jtib;
590  if (fwdflag.at(jtrack) != 1)
591  {
592  jmtif = jtib;
593  jmtib = jtif;
594  }
595 
596  for (size_t i=0; i<jmtif.size(); ++i)
597  {
598  mtif.push_back(jmtif.at(i));
599  }
600  for (size_t i=0; i<jmtib.size(); ++i)
601  {
602  mtib.push_back(jmtib.at(i));
603  }
604  TrackIoniz tim;
605  tim.setData(mtif,mtib);
606  ionCol->push_back(tim);
607  auto const ionizpointer = ionizPtrMaker(ionCol->size()-1);
608  ionTrkAssns->addSingle(ionizpointer, trackpointer);
609 
610  for (size_t ivtx = 0; ivtx < VerticesFromInputTracks.at(itrack).size(); ++ivtx)
611  {
612  auto vtxp = VerticesFromInputTracks.at(itrack).at(ivtx);
613  auto trkend = *(VerticesFromInputTracks.data(itrack).at(ivtx));
614  if (fwdflag.at(itrack) != 1)
615  {
616  trkend = 1 - trkend;
617  }
618  rec::IDNumber vtxid = vtxp->getIDNumber();
619  size_t ivout = vtxoutmap[vtxid];
620  auto const vtxptr = vtxPtrMaker(ivout);
621  trkVtxAssns->addSingle(trackpointer,vtxptr,trkend);
622  }
623 
624  }
625  }
626 
627  e.put(std::move(trkCol));
628  e.put(std::move(TPCClusterCol));
629  e.put(std::move(TPCClusterTrkAssns));
630  e.put(std::move(ionCol));
631  e.put(std::move(ionTrkAssns));
632  e.put(std::move(vtxCol));
633  e.put(std::move(trkVtxAssns));
634  e.put(std::move(HitTPCClusterAssns));
635  }
636 
637  // returns true if trackA and trackB look like they were split across the cathode and need merging
638  // fwdflag variables say whether the beginning endpoint is the one on the stitched end (1) or the endpoint
639  // is the one on the stitched end (2). Both flags are set to zero if the track is not stitchable
640  // deltaX is the shift in X needed to be applied to track A, and -deltaX is to be applied to track B.
641 
642  bool tpccathodestitch::cathodematch(const rec::Track &atrack, const rec::Track &btrack, int &afw, int &bfw, float &deltaX)
643  {
644  afw = 0;
645  bfw = 0;
646  bool matchable = false;
647 
648  // may not need this yet -- but magnetic field will be a function of position someday
649  // auto const *magFieldService = gar::providerFrom<mag::MagneticFieldService>();
650  // G4ThreeVector zerovec(0,0,0);
651  // G4ThreeVector magfield = magFieldService->FieldAtPoint(zerovec);
652 
654  double xtpccent = geo->TPCXCent();
655  TVector3 xhat(1,0,0);
656  TVector3 xcentv = xhat*xtpccent;
657 
658  std::vector<TVector3> apt; // endpoint
659  std::vector<TVector3> bpt;
660  std::vector<TVector3> adir; // direction
661  std::vector<TVector3> bdir;
662 
663  apt.emplace_back(atrack.Vertex());
664  apt.emplace_back(atrack.End());
665  adir.emplace_back(atrack.VtxDir());
666  adir.emplace_back(atrack.EndDir());
667  bpt.emplace_back(btrack.Vertex());
668  bpt.emplace_back(btrack.End());
669  bdir.emplace_back(btrack.VtxDir());
670  bdir.emplace_back(btrack.EndDir());
671 
672  // center the postions on the center of the detector
673 
674  for (size_t i=0; i<apt.size(); ++i) apt.at(i) -= xcentv;
675  for (size_t i=0; i<bpt.size(); ++i) bpt.at(i) -= xcentv;
676 
677  std::vector<int> fwflag = {2, 1};
678 
679  for (size_t ia=0; ia<apt.size(); ++ia)
680  {
681  for (size_t ib=0; ib<bpt.size(); ++ib)
682  {
683  // directions should point along track pieces to stitch, so they should be back to back
684  TVector3 v=adir.at(ia);
685  TVector3 u=bdir.at(ib);
686 
687  if (v.Dot(u) > -fCTCut) continue;
688 
689  // simple comparisons of vertex positions. Ignores the fact that we may be missing
690  // part of one of the tracks near the endpoint due to the gaps or patrec failure
691  //if ( ((apt.at(ia)-bpt.at(ib)).Cross(xhat)).Mag() > dYZCut) continue;
692  //if (TMath::Abs(apt.at(ia).X() + bpt.at(ib).X()) > fdXCut) continue;
693 
694  // solved for X shift called tau
695 
696  float tdenom = 1.0 - TMath::Sq(xhat.Dot(v));
697  if (tdenom == 0) continue;
698  TVector3 d = apt.at(ia) - bpt.at(ib);
699  float tau = (0.5/tdenom) * d.Dot( (v.Dot(xhat))*v - xhat );
700 
701  if (tau > fMaxDX) continue;
702  if (tau < fMinDX) continue;
703 
704  // solve for displacement along adir.
705 
706  float gamma = -v.Dot(2*tau*xhat + d);
707  TVector3 chivec = d + gamma*v + 2.0*tau*xhat;
708  if (chivec.Mag() > fDistCut) continue;
709 
710  // we have a match
711  afw = fwflag.at(ia);
712  bfw = fwflag.at(1-ib);
713  deltaX = tau;
714  matchable = true;
715 
716  break; // just find the first match -- possible to match the same tracks with different endpoints?
717  }
718  if (matchable) break;
719  }
720  return matchable;
721  }
722 
723 
724 
726 
727  } // namespace rec
728 } // namespace gar
std::string fInputTrackLabel
input tracks and associations with TPCClusters. Get vertices associated with these tracks from the as...
rec
Definition: tracks.py:88
int TrackEnd
Definition: Track.h:32
tpccathodestitch(fhicl::ParameterSet const &p)
std::string string
Definition: nybbler.cc:12
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.h:20
const float * VtxDir() const
Definition: Track.h:141
int fPrintLevel
debug printout: 0: none, 1: just track parameters and residuals, 2: all
const double e
const float * Vertex() const
Definition: Track.h:139
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
void produce(art::Event &e) override
std::string fInputTPCClusterLabel
input TPC Clusters
def move(depos, offset)
Definition: depos.py:107
float fMinDX
cut on minimum translation of dX on one side.
std::string fInputVertexLabel
input vertices and associations with tracks
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
Definition: DataViewImpl.h:441
p
Definition: test.py:223
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
float fDistCut
cut in distance between best-matching points
double gamma(double KE, const simb::MCParticle *part)
const float * End() const
Definition: Track.h:140
float fMaxDX
cut on maximum translation of dX on one side.
tpccathodestitch & operator=(tpccathodestitch const &)=delete
double getTime() const
Definition: TrackPar.cxx:207
General GArSoft Utilities.
void setXBeg(const float xbeg)
Definition: TrackPar.cxx:271
void setData(std::vector< std::pair< float, float >> dSigdX_FWD, std::vector< std::pair< float, float >> dSigdX_BAK)
Definition: TrackIoniz.cxx:20
std::string fInputHitLabel
needed to make TPCCluster - hit associations
float fCTCut
cut on cosine of angle matching
#define MF_LOG_WARNING(category)
bool cathodematch(const gar::rec::Track &trackA, const gar::rec::Track &trackB, int &fwdflagA, int &fwdflagB, float &deltaX)
LArSoft geometry interface.
Definition: ChannelGeo.h:16
art framework interface to geometry description
size_t IDNumber
Definition: IDNumberGen.h:71
QTextStream & endl(QTextStream &s)
const float * EndDir() const
Definition: Track.h:142