All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sbndcode/sbndcode/gallery/galleryAnalysis/MCAssociations.cpp
Go to the documentation of this file.
1 /**
2  * @file MCAssociations.cpp
3  * @brief Does something with the tracks (implementation file).
4  * @author Tracy Usher (usher@slac.stanford.edu
5  * @date October 24, 2017
6  * @see MCAssociations.h
7  *
8  */
9 
10 #include "MCAssociations.h"
11 
12 // LArSoft libraries
13 #include "nusimdata/SimulationBase/MCTruth.h"
16 
17 // canvas libraries
18 #include "canvas/Persistency/Common/FindMany.h"
19 
20 // ROOT libraries
21 #include "TVector3.h"
22 
23 // C/C++ standard libraries
24 #include <algorithm> // std::count_if()
25 
26 
27 MCAssociations::MCAssociations(fhicl::ParameterSet const& config)
28  : fHitProducerLabel(config.get<art::InputTag>("HitProducerLabel", "")),
29  fMCTruthProducerLabel(config.get<art::InputTag>("MCTruthProducerLabel", "")),
30  fAssnsProducerLabel(config.get<art::InputTag>("MCTrackAssnsProducerLabel","")),
31  fTrackProducerLabel(config.get<art::InputTag>("TrackProducerLabel","")),
32  fLocalDirName(config.get<std::string>("LocalDirName", ""))
33  {}
34 
36  const detinfo::DetectorPropertiesData& detectorProperties,
37  TDirectory* outDir)
38 {
40  fDetectorProperties = &detectorProperties;
41  fDir = outDir->mkdir(fLocalDirName.c_str());
42 }
43 
45 {
46  if (fDir)
47  {
48  fNTracks = std::make_unique<TH1F>("NTrueTracks", "Number of tracks;number of tracks;events", 100, 0., 200.);
49  fNTracks->SetDirectory(fDir);
50  fNHitsPerTrack = std::make_unique<TH1F>("NHitsPerTrack", "Number Hits/Track", 250, 0., 250.);
51  fNHitsPerTrack->SetDirectory(fDir);
52  fTrackLength = std::make_unique<TH1F>("TrackLength", "Length; track length(cm)", 200, 0., 200.);
53  fTrackLength->SetDirectory(fDir);
54  fTrackLenVsHits = std::make_unique<TH2F>("TrackLenVsHits", "Length;Hits", 200, 0., 200., 250, 0., 250.);
55 
56  fNHitsPerPrimary = std::make_unique<TH1F>("NHitsPerPrimary", "Number Hits/Primary;log(# hits)", 15, 0.5, 3.5);
57  fNHitsPerPrimary->SetDirectory(fDir);
58  fPrimaryLength = std::make_unique<TH1F>("PrimaryLength", "Length of Primary;length(cm)", 50, 0., 350.);
59  fPrimaryLength->SetDirectory(fDir);
60  fPrimaryLenVsHits = std::make_unique<TH2F>("PrimaryLenVsHits", "Length;Hits", 50, 0., 350., 250, 0., 2500.);
61  fPrimaryLenVsHits->SetDirectory(fDir);
62 
63  fNHitsPerReco = std::make_unique<TH1F>("PrimaryRecoNHits", "Number Hits/Track;log(# hits)", 15, 0.5, 3.5);
64  fNHitsPerReco->SetDirectory(fDir);
65  fDeltaNHits = std::make_unique<TH1F>("DeltaNHits", "Delta Number Hits", 100, -200., 200.);
66  fDeltaNHits->SetDirectory(fDir);
67  fPrimaryRecoLength = std::make_unique<TH1F>("PrimaryRecLength", "Length of Reco; length(cm)", 50, 0., 350.);
68  fPrimaryRecoLength->SetDirectory(fDir);
69  fDeltaTrackLen = std::make_unique<TH1F>("DeltaTrackLen", "Delta Track Length", 100, -100., 100.);
70  fDeltaTrackLen->SetDirectory(fDir);
71 
72  fPrimaryEfficiency = std::make_unique<TH1F>("PrimaryEfficiency", "Efficiency", 101, 0., 1.01);
73  fPrimaryEfficiency->SetDirectory(fDir);
74  fPrimaryCompleteness = std::make_unique<TH1F>("PrimaryCompleteness", "Completeness", 101, 0., 1.01);
75  fPrimaryCompleteness->SetDirectory(fDir);
76  fPrimaryPurity = std::make_unique<TH1F>("PrimaryPurity", "Purity", 101, 0., 1.01);
77  fPrimaryPurity->SetDirectory(fDir);
78 
79  fPrimaryEffVsMom = std::make_unique<TProfile>("PrimaryEffVsMom", "Efficiency vs Momentum;Momentum(GeV/c);Efficiency", 25, 0.1, 1.10, 0., 1.1);
80  fPrimaryEffVsMom->SetDirectory(fDir);
81  fPrimaryCompVsMom = std::make_unique<TProfile>("PrimaryCompVsMom", "Completeness vs Momentum;Momentum(GeV/c);Completeness", 25, 0.1, 1.10, 0., 1.1);
82  fPrimaryCompVsMom->SetDirectory(fDir);
83  fPrimaryPurityVsMom = std::make_unique<TProfile>("PrimaryPurVsMom", "Purity vs Momentum;Momentum(GeV/c);Purity", 25, 0.1, 1.10, 0., 1.1);
84  fPrimaryPurityVsMom->SetDirectory(fDir);
85 
86  fPrimaryEffVsLen = std::make_unique<TProfile>("PrimaryEffVsLen", "Efficiency vs Length; length; Efficiency", 30, 0., 300., 0., 1.1);
87  fPrimaryEffVsLen->SetDirectory(fDir);
88  fPrimaryCompVsLen = std::make_unique<TProfile>("PrimaryCompVsLen", "Completeness vs Length; length; Completeness", 30, 0., 300., 0., 1.1);
89  fPrimaryCompVsLen->SetDirectory(fDir);
90  fPrimaryPurityVsLen = std::make_unique<TProfile>("PrimaryPurVsLen", "Purity vs Length; length; Purity", 30, 0., 300., 0., 1.1);
91  fPrimaryPurityVsLen->SetDirectory(fDir);
92 
93  fPrimaryEffVsHits = std::make_unique<TProfile>("PrimaryEffVsHits", "Efficiency vs # Hits", 50, 0., 2000., 0., 1.1);
94  fPrimaryEffVsHits->SetDirectory(fDir);
95  fPrimaryCompVsHits = std::make_unique<TProfile>("PrimaryCompVsHits", "Completeness vs # Hits", 50, 0., 2000., 0., 1.1);
96  fPrimaryCompVsHits->SetDirectory(fDir);
97  fPrimaryPurityVsHits = std::make_unique<TProfile>("PrimaryPurityVsHits", "Purity vs # Hits", 50, 0., 2000., 0., 1.1);
98  fPrimaryPurityVsHits->SetDirectory(fDir);
99 
100  fPrimaryEffVsLogHits = std::make_unique<TProfile>("PrimaryEffVsLogHits", "Efficiency vs log(# Hits);log(# Hits);Efficiency", 15, 0.5, 3.5, 0., 1.1);
101  fPrimaryEffVsLogHits->SetDirectory(fDir);
102  fPrimaryCompVsLogHits = std::make_unique<TProfile>("PrimaryCompVsLogHits", "Completeness vs log(# Hits);log(# Hits);Completeness)", 15, 0.5, 3.5, 0., 1.1);
103  fPrimaryCompVsLogHits->SetDirectory(fDir);
104  fPrimaryPurityVsLogHits = std::make_unique<TProfile>("PrimaryPurityVsLogHits", "Purity vs log(# Hits);log(# Hits);Purity)", 15, 0.5, 3.5, 0., 1.1);
105  fPrimaryPurityVsLogHits->SetDirectory(fDir);
106  }
107  else
108  {
109  fNTracks.reset();
110  fNHitsPerTrack.reset();
111  fNHitsPerPrimary.reset();
112  }
113 
114  return;
115 } // MCAssociations::prepare()
116 
117 void MCAssociations::doTrackHitMCAssociations(gallery::Event& event)
118 {
119  // First step is to recover the MCTruth object vector...
120  const auto& mcParticleHandle = event.getValidHandle<std::vector<simb::MCParticle>>(fMCTruthProducerLabel);
121 
122  // We also need to recover the hit producer info
123  const auto& hitHandle = event.getValidHandle<std::vector<recob::Hit>>(fHitProducerLabel);
124 
125  // Now see how many reco hits might be associated to this particle
126  art::FindMany<recob::Hit, anab::BackTrackerHitMatchingData> hitsPerMCParticle(mcParticleHandle, event, fAssnsProducerLabel);
127 
128  // Make maps, we really like to make maps
129  using HitToPartVecMap = std::map<const recob::Hit*,std::set<const simb::MCParticle*>>;
130  using PartToHitVecMap = std::map<const simb::MCParticle*, std::set<const recob::Hit*>>;
131 
132  HitToPartVecMap hitToPartVecMap;
133  PartToHitVecMap partToHitVecMap;
134 
135  // Loop through the particles
136  for(int mcIdx = 0; mcIdx < mcParticleHandle->size(); mcIdx++)
137  {
138  try
139  {
140  const simb::MCParticle& mcParticle = mcParticleHandle->at(mcIdx);
141 
142  std::vector<const recob::Hit*> hitsVec = hitsPerMCParticle.at(mcIdx);
143 
144  for(const auto& hit : hitsVec)
145  {
146  hitToPartVecMap[hit].insert(&mcParticle);
147  partToHitVecMap[&mcParticle].insert(hit);
148  }
149  }
150  catch(...) {break;}
151  }
152 
153  // In this section try looking at tracking. Eventually we want to move this out of here...
154  // First step is to recover the MCTruth object vector...
155  const auto& trackHandle = event.getValidHandle<std::vector<recob::Track>>(fTrackProducerLabel);
156 
157  // Now see how many reco hits might be associated to this particle
158  art::FindMany<recob::Hit> hitsPerTrack(trackHandle, event, fTrackProducerLabel);
159 
160  // Define a mapping between tracks and associated MCParticles
161  using TrackToParticleSetMap = std::map<const recob::Track*, std::set<const simb::MCParticle*>>;
162  using ParticleToTrackSetMap = std::map<const simb::MCParticle*, std::set<const recob::Track*>>;
163  using ParticleToHitSetMap = std::map<const simb::MCParticle*, std::set<const recob::Hit*>>;
164  using TrackToPartHitSetMap = std::map<const recob::Track*, ParticleToHitSetMap>;
165  using TrackToHitsVecMap = std::map<const recob::Track*, std::vector<const recob::Hit*>>;
166 
167  TrackToParticleSetMap trackToParticleSetMap;
168  ParticleToTrackSetMap particleToTrackSetMap;
169 
170  TrackToPartHitSetMap trackToPartHitSetMap;
171  TrackToHitsVecMap trackToHitsVecMap;
172 
173  // Loop through the tracks and associate via the hits to MCParticles
174  for(int trkIdx = 0; trkIdx < trackHandle->size(); trkIdx++)
175  {
176  const recob::Track& track = trackHandle->at(trkIdx);
177 
178  std::vector<const recob::Hit*> hitsVec = hitsPerTrack.at(trkIdx);
179 
180  trackToHitsVecMap[&track] = hitsVec;
181 
182  ParticleToHitSetMap& particleToHitSetMap = trackToPartHitSetMap[&track];
183 
184  for(const auto& hit : hitsVec)
185  {
186  HitToPartVecMap::iterator partItr = hitToPartVecMap.find(hit);
187 
188  if (partItr != hitToPartVecMap.end())
189  {
190  for(const auto& particle : partItr->second)
191  {
192  particleToHitSetMap[particle].insert(hit);
193  trackToParticleSetMap[&track].insert(particle);
194  particleToTrackSetMap[particle].insert(&track);
195  }
196  }
197  }
198  }
199 
200  // *****************************************************************************************
201  // The bits below here should eventually be moved into their own analyzer algorithm
202  // but we are in a hurry now so do it all here...
203  // Ok, at this point we should be able to relate MCParticles to tracks and hits
204  // Let's start by just looking at the primary particle
205  const simb::MCParticle& primaryParticle = mcParticleHandle->at(0);
206 
207  ParticleToTrackSetMap::iterator partTrackItr = particleToTrackSetMap.find(&primaryParticle);
208 
209  // Define the parameters we want...
210  int numPrimaryHitsTotal = partToHitVecMap[&primaryParticle].size();
211 
212  // If there are NO reconstructed hits associated to this particle then we don't count
213  // But this should really be a check on fiducial volume I think...
214  if (numPrimaryHitsTotal > 0)
215  {
216  const recob::Track* bestTrack(0);
217  float efficiency(0.);
218  float completeness(0.);
219  float purity(0.);
220  int numTrackHits(0);
221 
222  // Here we find the best matched track to the MCParticle.
223  // Nothing exciting, most hits wins sort of thing...
224  if (partTrackItr != particleToTrackSetMap.end())
225  {
226  // Recover the longest track...
227  for(const auto& track : partTrackItr->second)
228  {
229  if (trackToPartHitSetMap[track][partTrackItr->first].size() > numTrackHits)
230  {
231  bestTrack = track;
232  numTrackHits = trackToPartHitSetMap[track][partTrackItr->first].size();
233  }
234  }
235 
236  if (bestTrack)
237  {
238  int numPrimaryHitsMatch = numTrackHits;
239  int numTrackHitsTotal = trackToHitsVecMap[bestTrack].size();
240 
241  completeness = float(numPrimaryHitsMatch) / float(numPrimaryHitsTotal);
242  purity = float(numPrimaryHitsMatch) / float(numTrackHitsTotal);
243 
244  if (completeness > 0.2) efficiency = 1.;
245  }
246  }
247 
248  // Calculate the length of this mc particle inside the fiducial volume.
249  TVector3 mcstart;
250  TVector3 mcend;
251  TVector3 mcstartmom;
252  TVector3 mcendmom;
253 
254  double xOffset(0.);
255 
256  double mcTrackLen = length(primaryParticle, xOffset, mcstart, mcend, mcstartmom, mcendmom);
257  double trackLen = 0.;
258 
259  if (bestTrack) trackLen = length(bestTrack);
260 
261  fNTracks->Fill(partToHitVecMap.size(), 1.);
262  fNHitsPerPrimary->Fill(std::log10(double(partToHitVecMap[&primaryParticle].size())), 1.);
263  fPrimaryLength->Fill(mcTrackLen, 1.);
264  fPrimaryLenVsHits->Fill(mcTrackLen, partToHitVecMap[&primaryParticle].size(), 1.);
265 
266  fPrimaryRecoLength->Fill(trackLen, 1.);
267  fDeltaTrackLen->Fill(trackLen-mcTrackLen, 1.);
268 
269  fNHitsPerReco->Fill(std::log10(numTrackHits), 1.);
270  fDeltaNHits->Fill(numTrackHits - int(partToHitVecMap[&primaryParticle].size()), 1.);
271 
272  // Loop through the particles again to histogram some secondary info...
273  for(int mcIdx = 0; mcIdx < mcParticleHandle->size(); mcIdx++)
274  {
275  try
276  {
277  const simb::MCParticle& mcParticle = mcParticleHandle->at(mcIdx);
278 
279  if (!partToHitVecMap[&mcParticle].empty())
280  {
281  // Calculate the length of this mc particle inside the fiducial volume.
282  double secTrackLen = length(mcParticle, xOffset, mcstart, mcend, mcstartmom, mcendmom);
283 
284  fNHitsPerTrack->Fill(partToHitVecMap[&mcParticle].size(), 1.);
285  fTrackLength->Fill(secTrackLen, 1.);
286  fTrackLenVsHits->Fill(secTrackLen, partToHitVecMap[&mcParticle].size(), 1.);
287  }
288  }
289  catch(...) {break;}
290  }
291 
292  // Final sets of plots
293  fPrimaryEfficiency->Fill(efficiency, 1.);
294  fPrimaryCompleteness->Fill(completeness, 1.);
295  fPrimaryPurity->Fill(purity, 1.);
296 
297  fPrimaryEffVsHits->Fill(numPrimaryHitsTotal, efficiency, 1.);
298  fPrimaryCompVsHits->Fill(numPrimaryHitsTotal, completeness, 1.);
299  fPrimaryPurityVsHits->Fill(numPrimaryHitsTotal, purity, 1.);
300 
301  double partMom = primaryParticle.P();
302  fPrimaryEffVsMom->Fill(partMom, efficiency, 1.);
303  fPrimaryCompVsMom->Fill(partMom, completeness, 1.);
304  fPrimaryPurityVsMom->Fill(partMom, purity, 1.);
305 
306  fPrimaryEffVsLen->Fill(mcTrackLen, efficiency, 1.);
307  fPrimaryCompVsLen->Fill(mcTrackLen, completeness, 1.);
308  fPrimaryPurityVsLen->Fill(mcTrackLen, purity, 1.);
309 
310  double logNumHits = std::log10(numPrimaryHitsTotal);
311  fPrimaryEffVsLogHits->Fill(logNumHits, efficiency, 1.);
312  fPrimaryCompVsLogHits->Fill(logNumHits, completeness, 1.);
313  fPrimaryPurityVsLogHits->Fill(logNumHits, purity, 1.);
314  }
315 
316  return;
317 } // MCAssociations::processTracks()
318 
320 {
321  if (fNTracks)
322  {
323  fDir->cd();
324  fNTracks->Write();
325  fNHitsPerTrack->Write();
326  fTrackLength->Write();
327  fTrackLenVsHits->Write();
328  fNHitsPerPrimary->Write();
329  fPrimaryLength->Write();
330  fPrimaryLenVsHits->Write();
331  fPrimaryEfficiency->Write();
332  fPrimaryCompleteness->Write();
333  fPrimaryPurity->Write();
334  fPrimaryEffVsHits->Write();
335  fPrimaryCompVsHits->Write();
336  fPrimaryPurityVsHits->Write();
337  fPrimaryEffVsMom->Write();
338  fPrimaryCompVsMom->Write();
339  fPrimaryPurityVsMom->Write();
340  fPrimaryEffVsLen->Write();
341  fPrimaryCompVsLen->Write();
342  fPrimaryPurityVsLen->Write();
343  fPrimaryEffVsLogHits->Write();
344  fPrimaryCompVsLogHits->Write();
345  fPrimaryPurityVsLogHits->Write();
346  fPrimaryRecoLength->Write();
347  fDeltaTrackLen->Write();
348  fNHitsPerReco->Write();
349  fDeltaNHits->Write();
350  }
351 } // MCAssociations::finish()
352 
353 // Length of reconstructed track.
354 //----------------------------------------------------------------------------
355 double MCAssociations::length(const recob::Track* track) const
356 {
357  return track->Length();
358 }
359 
360 // Length of MC particle.
361 //----------------------------------------------------------------------------
362 double MCAssociations::length(const simb::MCParticle& part, double dx,
363  TVector3& start, TVector3& end, TVector3& startmom, TVector3& endmom,
364  unsigned int tpc, unsigned int cstat) const
365 {
366  // Need the readout window size...
367  double readOutWindowSize = fDetectorProperties->ReadOutWindowSize();
368 
369  double result = 0.;
370  TVector3 disp;
371  int n = part.NumberTrajectoryPoints();
372  bool first = true;
373 
374  // The following for debugging purposes
375  int findTrackID(-1);
376 
377  if (part.TrackId() == findTrackID) std::cout << ">>> length, mcpart: " << part << std::endl;
378 
379  // Loop over the complete collection of trajectory points
380  for(int i = 0; i < n; ++i)
381  {
382  TVector3 posInTPC(0.,0.,0.);
383  TVector3 posVec = part.Position(i).Vect();
384  double pos[] = {posVec.X(),posVec.Y(),posVec.Z()};
385 
386  // Try identifying the TPC we are in
387  unsigned int tpc(0);
388  unsigned int cstat(0);
389 
390  // Need to make sure this position is in an active region of the TPC
391  // If the particle is not in the cryostat then we skip
392  try
393  {
394  const geo::TPCGeo& tpcGeo = fGeometry->PositionToTPC(pos, tpc, cstat);
395 
396  TVector3 activePos = posVec - tpcGeo.GetActiveVolumeCenter();
397 
398  if (part.TrackId() == findTrackID)
399  std::cout << " --> traj point: " << i << ", pos: " << posVec.X() << "/" << posVec.Y() << "/" << posVec.Z() << ", active pos: " << activePos.X() << "/" << activePos.Y() << "/" << activePos.Z() << std::endl;
400 
401  if (std::fabs(activePos.X()) > tpcGeo.ActiveHalfWidth() || std::fabs(activePos.Y()) > tpcGeo.ActiveHalfHeight() || std::fabs(activePos.Z()) > 0.5 * tpcGeo.ActiveLength()) continue;
402 
403  posInTPC = TVector3(activePos.X() + tpcGeo.ActiveHalfWidth(), activePos.Y(), activePos.Z());
404  } catch(...) {continue;}
405 
406  // Make fiducial cuts.
407  // There are two sets here:
408  // 1) We check the original x,y,z position of the trajectory points and require they be
409  // within the confines of the physical TPC
410  // 2) We then check the timing of the presumed hit and make sure it lies within the
411  // readout window for this simulation
412  pos[0] += dx;
413  double ticks = fDetectorProperties->ConvertXToTicks(posInTPC.X(), 0, tpc, cstat);
414 
415  if (part.TrackId() == findTrackID)
416  std::cout << " ==> tpc: " << tpc << ", cstat: " << cstat << ", ticks: " << ticks << std::endl;
417 
418  // Currently it appears that the detector properties are not getting initialized properly and the returned
419  // number of ticks is garbage so we need to skip this for now. Will be important when we get CR's
420 // if(ticks >= 0. && ticks < readOutWindowSize)
421  {
422  if(first)
423  {
424  start = pos;
425  startmom = part.Momentum(i).Vect();
426  }
427  else
428  {
429  disp -= pos;
430  result += disp.Mag();
431  }
432  first = false;
433  disp = pos;
434  end = pos;
435  endmom = part.Momentum(i).Vect();
436  }
437 
438  if (part.TrackId() == findTrackID)
439  {
440  try
441  {
442  std::cout << ">>> Track #" << findTrackID << ", pos: " << posVec.X() << ", " << posVec.Y() << ", " << posVec.Z() << ", ticks: " << ticks << ", nearest Y wire: ";
443  geo::WireID wireID = fGeometry->NearestWireID(pos, 2);
444  std::cout << wireID << std::endl;
445  }
446  catch(...) {}
447  }
448  }
449 
450  return result;
451 }
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
Point GetActiveVolumeCenter() const
Returns the center of the TPC active volume in world coordinates [cm].
Definition: TPCGeo.h:288
const geo::GeometryCore * geometry
double ActiveHalfHeight() const
Half height (associated with y coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:99
Declaration of signal hit object.
Geometry information for a single TPC.
Definition: TPCGeo.h:38
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
process_name use argoneut_mc_hitfinder track
process_name hit
Definition: cheaterreco.fcl:51
tick ticks
Alias for common language habits.
Definition: electronics.h:78
geo::TPCGeo const & PositionToTPC(geo::Point_t const &point) const
Returns the TPC at specified location.
double Length(size_t p=0) const
Access to various track properties.
double ActiveHalfWidth() const
Half width (associated with x coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:95
std::unique_ptr< detinfo::DetectorPropertiesData const > fDetectorProperties
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
Description of geometry of one entire detector.
double ActiveLength() const
Length (associated with z coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:103
geo::WireID NearestWireID(geo::Point_t const &point, geo::PlaneID const &planeid) const
Returns the ID of wire closest to position in the specified TPC.
bool empty(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:555
BEGIN_PROLOG could also be cout
Track from a non-cascading particle.A recob::Track consists of a recob::TrackTrajectory, plus additional members relevant for a &quot;fitted&quot; track:
void setup(const geo::GeometryCore &, const detinfo::DetectorPropertiesData &, TDirectory *)