All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NuMuEfficiencyStudy_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 // Class: NuMuEfficiencyStudy
3 // Plugin Type: analyzer (art v3_03_01)
4 // File: NuMuEfficiencyStudy_module.cc
5 //
6 // Generated at Wed Mar 25 12:10:37 2020 by Gray Putnam using cetskelgen
7 // from cetlib version v3_08_00.
8 ////////////////////////////////////////////////////////////////////////
9 
10 #include "art/Framework/Core/EDAnalyzer.h"
11 #include "art/Framework/Core/ModuleMacros.h"
12 #include "art/Framework/Principal/Event.h"
13 #include "art/Framework/Principal/Handle.h"
14 #include "art/Framework/Principal/Run.h"
15 #include "art/Framework/Principal/SubRun.h"
16 #include "canvas/Utilities/InputTag.h"
17 #include "fhiclcpp/ParameterSet.h"
18 #include "messagefacility/MessageLogger/MessageLogger.h"
19 
20 #include "art/Framework/Services/Registry/ServiceHandle.h"
24 
25 // LArSoft includes
32 #include "art_root_io/TFileService.h"
33 
35 
37 
49 #include "nusimdata/SimulationBase/MCParticle.h"
50 #include "nusimdata/SimulationBase/MCTruth.h"
51 
53 
54 #include "TH1D.h"
56 
57 #include <numeric> // std::accumulate
58 
59 namespace numu {
60  class NuMuEfficiencyStudy;
61 }
62 
63 struct Histos {
66  TH1D *muon_length;
67  TH1D *vertex_x;
68  TH1D *vertex_y;
69  TH1D *vertex_z;
70 
72  TH1D *muon_purity;
74  TH1D *slice_purity;
75 };
76 
77 float ContainedLength(const TVector3 &v0, const TVector3 &v1,
78  const std::vector<geoalgo::AABox> &boxes);
79 
80 const simb::MCParticle *Genie2G4MCParticle(
81  const simb::MCParticle &genie_part,
82  const simb::MCTruth &mctruth,
83  const std::vector<art::Ptr<simb::MCParticle>> &g4_mcparticles,
84  const std::vector<const sim::GeneratedParticleInfo *> infos);
85 
86 class numu::NuMuEfficiencyStudy : public art::EDAnalyzer {
87 public:
88  explicit NuMuEfficiencyStudy(fhicl::ParameterSet const& p);
89  // The compiler-generated destructor is fine for non-base
90  // classes without bare pointers or other resource use.
91 
92  // Plugins should not be copied or assigned.
97 
98  // Required functions.
99  void analyze(art::Event const& e) override;
100 
101 private:
103  std::vector<Histos> fReco;
104 
105  std::vector<std::vector<geo::BoxBoundedGeo>> fTPCVolumes;
106  std::vector<geo::BoxBoundedGeo> fActiveVolumes;
107 
108  static const unsigned n_reco_eff = 14;
109 
110  std::array<float, 6> fFiducialInset;
111  std::vector<geo::BoxBoundedGeo> fFiducialVolumes;
112 
113  bool InFV(TVector3 pos);
114 
115  void InitHistos();
116 
117  void InitHisto(Histos &h, const std::string &prefix);
118 
119  void FillTrue(Histos &h, const simb::MCTruth &interaction, const simb::MCParticle &G4lepton);
120 
121  void FillMatchingMu(Histos &h, float purity, float completeness);
122  void FillMatchingSlc(Histos &h, float purity, float completeness);
123 
124  float ParticleLength(const simb::MCParticle &particle);
125 
126 };
127 
129  for (auto const &FV: fFiducialVolumes) {
130  if (FV.ContainsPosition(pos)) return true;
131  }
132  return false;
133 }
134 
136  InitHisto(fTruth, "truth");
137 
138  std::array<std::string, n_reco_eff> effnames {"has_muon",
139  "has_reco_muon",
140  "muon_is_fid",
141  "has_slice",
142  "has_pure_slice",
143  "has_reco_slice",
144  "has_fid_slice",
145  "slice_has_muon",
146  "has_fid_pure_slice",
147  "has_reco_fid_slice",
148  "slice_has_reco_muon",
149  "has_fid_nu_slice",
150  "has_pure_nu_slice",
151  "has_neutrino_slice"};
152  for (unsigned i = 0; i < n_reco_eff; i++) {
153  for (unsigned j = 0; j < n_reco_eff; j++) {
154  std::string name;
155  if (i == j) name = effnames[i];
156  else name = effnames[i] + "_" + effnames[j];
157  fReco.emplace_back();
158  InitHisto(fReco.back(), name);
159  }
160  }
161 }
162 
163 void numu::NuMuEfficiencyStudy::InitHisto(Histos &h, const std::string &prefix) {
164  art::ServiceHandle<art::TFileService> tfs;
165 
166  h.neutrino_energy = tfs->make<TH1D>((prefix + "_nuE").c_str(), "nuE", 100, 0, 4);
167  h.muon_momentum = tfs->make<TH1D>((prefix + "_muP").c_str(), "muP", 100, 0, 4);
168  h.muon_length = tfs->make<TH1D>((prefix + "_muL").c_str(), "muL", 100, 0, 600);
169 
170  float XMin = std::min_element(fFiducialVolumes.begin(), fFiducialVolumes.end(), [](auto &lhs, auto &rhs) { return lhs.MinX() < rhs.MinX(); })->MinX();
171  float XMax = std::max_element(fFiducialVolumes.begin(), fFiducialVolumes.end(), [](auto &lhs, auto &rhs) { return lhs.MaxX() < rhs.MaxX(); })->MaxX();
172  float YMin = std::min_element(fFiducialVolumes.begin(), fFiducialVolumes.end(), [](auto &lhs, auto &rhs) { return lhs.MinY() < rhs.MinY(); })->MinY();
173  float YMax = std::max_element(fFiducialVolumes.begin(), fFiducialVolumes.end(), [](auto &lhs, auto &rhs) { return lhs.MaxY() < rhs.MaxY(); })->MaxY();
174  float ZMin = std::min_element(fFiducialVolumes.begin(), fFiducialVolumes.end(), [](auto &lhs, auto &rhs) { return lhs.MinZ() < rhs.MinZ(); })->MinZ();
175  float ZMax = std::max_element(fFiducialVolumes.begin(), fFiducialVolumes.end(), [](auto &lhs, auto &rhs) { return lhs.MaxZ() < rhs.MaxZ(); })->MaxZ();
176 
177  h.vertex_x = tfs->make<TH1D>((prefix + "_v_x").c_str(), "v_x", 200, XMin, XMax);
178  h.vertex_y = tfs->make<TH1D>((prefix + "_v_y").c_str(), "v_y", 200, YMin, YMax);
179  h.vertex_z = tfs->make<TH1D>((prefix + "_v_z").c_str(), "v_z", 200, ZMin, ZMax);
180 
181  h.muon_completeness = tfs->make<TH1D>((prefix + "_muC").c_str(), "muC", 100, 0., 1.);
182  h.muon_purity = tfs->make<TH1D>((prefix + "_mupur").c_str(), "mupur", 100, 0., 1.);
183 
184  h.slice_completeness = tfs->make<TH1D>((prefix + "_nuC").c_str(), "nuC", 100, 0., 1.);
185  h.slice_purity = tfs->make<TH1D>((prefix + "_nupur").c_str(), "nupur", 100, 0., 1.);
186 }
187 
189  : EDAnalyzer{p},
190  fFiducialInset({p.get<float>("xmin"), p.get<float>("xmax"), p.get<float>("ymin"), p.get<float>("ymax"), p.get<float>("zmin"), p.get<float>("zmax")})
191  // More initializers here.
192 {
193 
194  const geo::GeometryCore *geometry = lar::providerFrom<geo::Geometry>();
195 
196  // first the TPC volumes
197  for (auto const &cryo: geometry->IterateCryostats()) {
198  geo::GeometryCore::TPC_iterator iTPC = geometry->begin_TPC(cryo.ID()),
199  tend = geometry->end_TPC(cryo.ID());
200  std::vector<geo::BoxBoundedGeo> this_tpc_volumes;
201  while (iTPC != tend) {
202  geo::TPCGeo const& TPC = *iTPC;
203  this_tpc_volumes.push_back(TPC.ActiveBoundingBox());
204  iTPC++;
205  }
206  fTPCVolumes.push_back(std::move(this_tpc_volumes));
207  }
208 
209  // then combine them into active volumes
210  for (const std::vector<geo::BoxBoundedGeo> &tpcs: fTPCVolumes) {
211  double XMin = std::min_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MinX() < rhs.MinX(); })->MinX();
212  double YMin = std::min_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MinY() < rhs.MinY(); })->MinY();
213  double ZMin = std::min_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MinZ() < rhs.MinZ(); })->MinZ();
214 
215  double XMax = std::max_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MaxX() < rhs.MaxX(); })->MaxX();
216  double YMax = std::max_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MaxY() < rhs.MaxY(); })->MaxY();
217  double ZMax = std::max_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MaxZ() < rhs.MaxZ(); })->MaxZ();
218 
219  fActiveVolumes.emplace_back(XMin, XMax, YMin, YMax, ZMin, ZMax);
220  fFiducialVolumes.emplace_back(XMin + fFiducialInset[0], XMax - fFiducialInset[1],
221  YMin + fFiducialInset[2], YMax - fFiducialInset[3],
222  ZMin + fFiducialInset[4], ZMax - fFiducialInset[5]);
223  }
224 
225  InitHistos();
226 }
227 
228 float numu::NuMuEfficiencyStudy::ParticleLength(const simb::MCParticle &particle) {
229  std::vector<geoalgo::AABox> aa_volumes;
230  for (const geo::BoxBoundedGeo &AV: fActiveVolumes) {
231  if (AV.ContainsPosition(particle.Position().Vect())) {
232  aa_volumes.emplace_back(AV.MinX(), AV.MinY(), AV.MinZ(), AV.MaxX(), AV.MaxY(), AV.MaxZ());
233  break;
234  }
235  }
236 
237  float length = 0.;
238  for (unsigned i = 1; i < particle.NumberTrajectoryPoints(); i++) {
239  length += ContainedLength(particle.Position(i-1).Vect(), particle.Position(i).Vect(), aa_volumes);
240  }
241 
242  return length;
243 }
244 
245 void numu::NuMuEfficiencyStudy::FillMatchingMu(Histos &h, float purity, float completeness) {
246  h.muon_completeness->Fill(completeness);
247  h.muon_purity->Fill(purity);
248 }
249 
250 void numu::NuMuEfficiencyStudy::FillMatchingSlc(Histos &h, float purity, float completeness) {
251  h.slice_completeness->Fill(completeness);
252  h.slice_purity->Fill(purity);
253 }
254 
255 void numu::NuMuEfficiencyStudy::FillTrue(Histos &h, const simb::MCTruth &truth, const simb::MCParticle &G4lepton) {
256  h.neutrino_energy->Fill(truth.GetNeutrino().Nu().E());
257  h.muon_momentum->Fill(truth.GetNeutrino().Lepton().Momentum().Vect().Mag());
258  h.muon_length->Fill(ParticleLength(G4lepton));
259 
260  h.vertex_x->Fill(truth.GetNeutrino().Nu().Position().X());
261  h.vertex_y->Fill(truth.GetNeutrino().Nu().Position().Y());
262  h.vertex_z->Fill(truth.GetNeutrino().Nu().Position().Z());
263 }
264 
265 void numu::NuMuEfficiencyStudy::analyze(art::Event const& ev)
266 {
267  art::ServiceHandle<cheat::BackTrackerService> backtracker;
268  art::ServiceHandle<cheat::ParticleInventoryService> inventory_service;
269 
270  auto const clock_data = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(ev);
271 
272  art::ValidHandle<std::vector<simb::MCTruth>> mctruth_handle = ev.getValidHandle<std::vector<simb::MCTruth>>("generator");
273  const std::vector<simb::MCTruth> &mctruth_list = *mctruth_handle;
274  std::vector<art::Ptr<simb::MCTruth>> mctruth_ptrs;
275  art::fill_ptr_vector(mctruth_ptrs, mctruth_handle);
276 
277  art::FindManyP<simb::MCParticle, sim::GeneratedParticleInfo> truth_to_particles(mctruth_handle, ev, "largeant");
278  const std::vector<simb::MCParticle> &g4_particles = *ev.getValidHandle<std::vector<simb::MCParticle>>("largeant");
279  std::map<int, const simb::MCParticle*> g4_particle_map;
280  for (unsigned i_g4 = 0; i_g4 < g4_particles.size(); i_g4++) {
281  g4_particle_map[g4_particles[i_g4].TrackId()] = &g4_particles[i_g4];
282  }
283 
284  std::vector<int> truth_to_numucc;
285  std::vector<simb::MCTruth> numucc;
286  std::vector<simb::MCParticle> G4muons;
287  // iterate over the true interactions
288  for (unsigned i = 0; i < mctruth_list.size(); i++) {
289  const simb::MCTruth &truth = mctruth_list[i];
290  TVector3 vertex(truth.GetNeutrino().Nu().Position().X(), truth.GetNeutrino().Nu().Position().Y(), truth.GetNeutrino().Nu().Position().Z());
291  // fiducial numu CC interactions
292  if (InFV(vertex) && truth.GetNeutrino().CCNC() == 0 && abs(truth.GetNeutrino().Nu().PdgCode()) == 14) {
293  // find the muon in G4
294  const simb::MCParticle *G4muon = Genie2G4MCParticle(truth.GetNeutrino().Lepton(), truth, truth_to_particles.at(i), truth_to_particles.data(i));
295  FillTrue(fTruth, truth, *G4muon);
296  numucc.push_back(truth);
297  G4muons.push_back(*G4muon);
298  truth_to_numucc.push_back(numucc.size()-1);
299  }
300  else truth_to_numucc.push_back(-1);
301  }
302 
303  // no true numu CC? Return
304  if (numucc.size() == 0) return;
305 
306  // get the deposited energy for each muon
307  std::vector<float> muonVisE(G4muons.size(), 0.);
308  for (unsigned i = 0; i < G4muons.size(); i++) {
309  std::vector<const sim::IDE*> particle_ides(backtracker->TrackIdToSimIDEs_Ps(G4muons[i].TrackId()));
310  for (const sim::IDE *ide: particle_ides) muonVisE[i] += ide->energy / 1000.;
311  }
312 
313  // get the deposited energy from primary particles in the neutrino
314  std::vector<float> numuVisE(numucc.size(), 0.);
315  for (unsigned i_g4 = 0; i_g4 < g4_particles.size(); i_g4++) {
316  const simb::MCParticle &particle = g4_particles[i_g4];
317  if (particle.Process() != "primary") continue;
318  art::Ptr<simb::MCTruth> truth = inventory_service->TrackIdToMCTruth_P(particle.TrackId());
319  for (unsigned i_truth = 0; i_truth < mctruth_ptrs.size(); i_truth++) {
320  if (truth == mctruth_ptrs[i_truth]) {
321  if (truth_to_numucc[i_truth] != -1) {
322  std::vector<const sim::IDE*> particle_ides(backtracker->TrackIdToSimIDEs_Ps(particle.TrackId()));
323  for (const sim::IDE *ide: particle_ides) numuVisE[truth_to_numucc[i_truth]] += ide->energy / 1000.;
324  }
325  break;
326  }
327  }
328  }
329 
330  // now collect the reco information
331  //
332  // does a muon track exist?
333  std::vector<int> matching_track(G4muons.size(), -1);
334  std::vector<float> track_purity(G4muons.size(), -1);
335  std::vector<float> track_completeness(G4muons.size(), -1);
336 
337  art::ValidHandle<std::vector<recob::PFParticle>> particle_handle = ev.getValidHandle<std::vector<recob::PFParticle>>("pandora");
338  art::FindManyP<recob::Track> tracks(particle_handle, ev, "pandoraTrack");
339  art::FindManyP<larpandoraobj::PFParticleMetadata> metadatas(particle_handle, ev, "pandora");
340  art::FindManyP<recob::Vertex> particles_to_vertex(particle_handle, ev, "pandora");
341  for (unsigned i_part = 0; i_part < particle_handle->size(); i_part++) {
342  if (!tracks.at(i_part).size()) continue;
343  art::FindManyP<recob::Hit> fmHits(tracks.at(i_part), ev, "pandoraTrack");
344  const std::vector<art::Ptr<recob::Hit>> &this_hits = fmHits.at(0);
345 
346  std::vector<std::pair<int, float>> matches = CAFRecoUtils::AllTrueParticleIDEnergyMatches(clock_data, this_hits);
347 
348  // sort by matching energy
349  std::sort(matches.begin(), matches.end(),
350  [] (auto const &lhs, auto const &rhs) { return lhs.second > rhs.second; });
351 
352  for (unsigned i_cc = 0; i_cc < G4muons.size(); i_cc++) {
353  if (matches.size() && matches[0].first == G4muons[i_cc].TrackId() && matches[0].second / muonVisE[i_cc] > 0.5) {
354  matching_track[i_cc] = particle_handle->at(i_part).Self();
355  track_purity[i_cc] = matches[0].second /
356  std::accumulate(matches.begin(), matches.end(), 0.f,
357  [](auto &a, auto &b){return a + b.second;});
358  track_completeness[i_cc] = matches[0].second / muonVisE[i_cc];
359  }
360  }
361  }
362 
363  // also find a matching slice
364  std::vector<int> matching_slice(numucc.size(), -1);
365  std::vector<float> matching_purity(numucc.size(), -1);
366  std::vector<float> matching_completeness(numucc.size(), -1);
367 
368  art::ValidHandle<std::vector<recob::Slice>> slice_handle = ev.getValidHandle<std::vector<recob::Slice>>("pandora");
369  art::FindManyP<recob::Hit> slice_hits(slice_handle, ev, "pandora");
370  art::FindManyP<recob::PFParticle> slice_particles(slice_handle, ev, "pandora");
371 
372  for (unsigned i_slice = 0; i_slice < slice_handle->size(); i_slice++) {
373  std::vector<float> matchingE(numuVisE.size(), 0.);
374  std::vector<float> matchingPrimaryE(numuVisE.size(), 0.);
375  float totalE = 0.;
376 
377  std::vector<std::pair<int, float>> matches = CAFRecoUtils::AllTrueParticleIDEnergyMatches(clock_data, slice_hits.at(i_slice));
378  for (unsigned i_match = 0; i_match < matches.size(); i_match++) {
379  totalE += matches[i_match].second / 1000.;
380  }
381 
382  for (unsigned i_match = 0; i_match < matches.size(); i_match++) {
383  if (g4_particle_map.count(matches[i_match].first)) {
384  const simb::MCParticle &part = *g4_particle_map.at(matches[i_match].first);
385  art::Ptr<simb::MCTruth> truth = inventory_service->TrackIdToMCTruth_P(part.TrackId());
386  for (unsigned i_truth = 0; i_truth < mctruth_ptrs.size(); i_truth++) {
387  if (truth == mctruth_ptrs[i_truth]) {
388  if (truth_to_numucc[i_truth] != -1) {
389  if (part.Process() == "primary") matchingPrimaryE[truth_to_numucc[i_truth]] += matches[i_match].second / 1000.;
390  matchingE[truth_to_numucc[i_truth]] += matches[i_match].second / 1000.;
391  }
392  break;
393  }
394  }
395  }
396  }
397 
398  unsigned best_match = std::distance(matchingE.begin(), std::max_element(matchingE.begin(), matchingE.end()));
399  if (matchingPrimaryE[best_match] / numuVisE[best_match] > 0.5) {
400  matching_slice[best_match] = i_slice;
401  matching_purity[best_match] = matchingE[best_match] / totalE;
402  matching_completeness[best_match] = matchingPrimaryE[best_match] / numuVisE[best_match];
403  }
404  }
405 
406  // gather information for efficiency calculation
407  for (unsigned i = 0; i < numucc.size(); i++) {
408  bool has_muon_track = matching_track[i] != -1;
409  bool has_slice = matching_slice[i] != -1;
410 
411  bool has_pure_slice = has_slice && matching_purity[i] > 0.5;
412 
413  bool muon_is_reco = false;
414  bool muon_is_fid = false;
415  if (has_muon_track) {
416  const larpandoraobj::PFParticleMetadata &meta = *metadatas.at(matching_track[i]).at(0);
417  auto const &properties = meta.GetPropertiesMap();
418  std::cout << "Muon Track Properties:\n";
419  for (auto pair: properties) std::cout << pair.first << std::endl;
420  if (properties.count("IsClearCosmic")) {
421  muon_is_reco = false;
422  }
423  else {
424  muon_is_reco = true;
425  }
426 
427  auto muon_vert = tracks.at(matching_track[i]).at(0)->Start();
428  TVector3 muon_vert_v(muon_vert.X(), muon_vert.Y(), muon_vert.Z());
429 
430  muon_is_fid = InFV(muon_vert_v);
431  }
432 
433  bool slice_is_neutrino = false;
434  bool slice_is_reco = false;
435  bool slice_is_fiducial = false;
436  bool slice_has_muon = false;
437  if (has_slice) {
438  const std::vector<art::Ptr<recob::PFParticle>> &this_slice_particles = slice_particles.at(matching_slice[i]);
439  for (art::Ptr<recob::PFParticle> pfp: this_slice_particles) {
440  if (pfp->IsPrimary()) {
441  const larpandoraobj::PFParticleMetadata &meta = *metadatas.at(pfp.key()).at(0);
442  auto const &properties = meta.GetPropertiesMap();
443  std::cout << "Particle Properties:\n";
444  for (auto pair: properties) std::cout << pair.first << " " << pair.second << std::endl;
445 
446  if (properties.count("IsNeutrino")) slice_is_neutrino = true;
447  if (properties.count("IsNeutrino") && pfp->PdgCode() == 14) slice_is_reco = true;
448 
449  // check for vertex
450  if (particles_to_vertex.at(pfp.key()).size()) {
451  const recob::Vertex &vert = *particles_to_vertex.at(pfp.key()).at(0);
452  TVector3 vect(vert.position().X(), vert.position().Y(), vert.position().Z());
453  slice_is_fiducial = InFV(vect);
454  }
455  }
456  if (has_muon_track && matching_track[i] == (int)pfp->Self()) {
457  slice_has_muon = true;
458  }
459  }
460  }
461 
462  bool has_pure_nu_slice = has_pure_slice && slice_is_neutrino;
463  bool has_fid_nu_slice = slice_is_fiducial && slice_is_neutrino;
464  bool has_fid_pure_slice = slice_is_fiducial && has_pure_slice;
465  bool has_reco_fid_slice = slice_is_fiducial && slice_is_reco;
466  bool slice_has_reco_muon = slice_has_muon && muon_is_reco;
467 
468  std::array<bool, n_reco_eff> pass = {has_muon_track, muon_is_reco, muon_is_fid, has_slice, has_pure_slice, slice_is_reco, slice_is_fiducial, slice_has_muon, has_fid_pure_slice, has_reco_fid_slice, slice_has_reco_muon, has_fid_nu_slice, has_pure_nu_slice, slice_is_neutrino};
469  for (unsigned j = 0; j < n_reco_eff; j++) {
470  for (unsigned k = 0; k < n_reco_eff; k++) {
471  if (pass[j] && pass[k]) {
472  FillTrue(fReco[j * n_reco_eff + k], numucc[i], G4muons[i]);
473  FillMatchingMu(fReco[j * n_reco_eff + k], track_purity[i], track_completeness[i]);
474  FillMatchingSlc(fReco[j * n_reco_eff + k], matching_purity[i], matching_completeness[i]);
475  }
476  }
477  }
478  }
479 
480 }
481 
482 float ContainedLength(const TVector3 &v0, const TVector3 &v1,
483  const std::vector<geoalgo::AABox> &boxes) {
484  static const geoalgo::GeoAlgo algo;
485 
486  // if points are the same, return 0
487  if ((v0 - v1).Mag() < 1e-6) return 0;
488 
489  // construct individual points
490  geoalgo::Point_t p0(v0);
491  geoalgo::Point_t p1(v1);
492 
493  // construct line segment
494  geoalgo::LineSegment line(p0, p1);
495 
496  double length = 0;
497 
498  // total contained length is sum of lengths in all boxes
499  // assuming they are non-overlapping
500  for (auto const &box: boxes) {
501  int n_contained = box.Contain(p0) + box.Contain(p1);
502  // both points contained -- length is total length (also can break out of loop)
503  if (n_contained == 2) {
504  length = (v1 - v0).Mag();
505  break;
506  }
507  // one contained -- have to find intersection point (which must exist)
508  if (n_contained == 1) {
509  auto intersections = algo.Intersection(line, box);
510  // Because of floating point errors, it can sometimes happen
511  // that there is 1 contained point but no "Intersections"
512  // if one of the points is right on the edge
513  if (intersections.size() == 0) {
514  // determine which point is on the edge
515  double tol = 1e-5;
516  bool p0_edge = algo.SqDist(p0, box) < tol;
517  bool p1_edge = algo.SqDist(p1, box) < tol;
518  assert(p0_edge || p1_edge);
519  // contained one is on edge -- can treat both as not contained
520  //
521  // In this case, no length
522  if ((p0_edge && box.Contain(p0)) || (box.Contain(p1) && p1_edge))
523  continue;
524  // un-contaned one is on edge -- treat both as contained
525  else if ((p0_edge && box.Contain(p1)) || (box.Contain(p0) && p1_edge)) {
526  length = (v1 - v0).Mag();
527  break;
528  }
529  else {
530  assert(false); // bad
531  }
532  }
533  // floating point errors can also falsely cause 2 intersection points
534  //
535  // in this case, one of the intersections must be very close to the
536  // "contained" point, so the total contained length will be about
537  // the same as the distance between the two intersection points
538  else if (intersections.size() == 2) {
539  length += (intersections.at(0).ToTLorentzVector().Vect() - intersections.at(1).ToTLorentzVector().Vect()).Mag();
540  continue;
541  }
542  // "Correct"/ideal case -- 1 intersection point
543  else if (intersections.size() == 1) {
544  // get TVector at intersection point
545  TVector3 int_tv(intersections.at(0).ToTLorentzVector().Vect());
546  length += ( box.Contain(p0) ? (v0 - int_tv).Mag() : (v1 - int_tv).Mag() );
547  }
548  else assert(false); // bad
549  }
550  // none contained -- either must have zero or two intersections
551  if (n_contained == 0) {
552  auto intersections = algo.Intersection(line, box);
553  if (!(intersections.size() == 0 || intersections.size() == 2)) {
554  // more floating point error fixes...
555  //
556  // figure out which points are near the edge
557  double tol = 1e-5;
558  bool p0_edge = algo.SqDist(p0, box) < tol;
559  bool p1_edge = algo.SqDist(p1, box) < tol;
560  // and which points are near the intersection
561  TVector3 vint = intersections.at(0).ToTLorentzVector().Vect();
562 
563  bool p0_int = (v0 - vint).Mag() < tol;
564  bool p1_int = (v1 - vint).Mag() < tol;
565  // exactly one of them should produce the intersection
566  assert((p0_int && p0_edge) != (p1_int && p1_edge));
567  // void variables when assert-ions are turned off
568  (void) p0_int; (void) p1_int;
569 
570  // both close to edge -- full length is contained
571  if (p0_edge && p1_edge) {
572  length += (v0 - v1).Mag();
573  }
574  // otherwise -- one of them is not on an edge, no length is contained
575  else {}
576  }
577  // assert(intersections.size() == 0 || intersections.size() == 2);
578  else if (intersections.size() == 2) {
579  TVector3 start(intersections.at(0).ToTLorentzVector().Vect());
580  TVector3 end(intersections.at(1).ToTLorentzVector().Vect());
581  length += (start - end).Mag();
582  }
583  }
584  }
585 
586  return length;
587 }//ContainedLength
588 
589 DEFINE_ART_MODULE(numu::NuMuEfficiencyStudy)
590 
591 const simb::MCParticle *Genie2G4MCParticle(
592  const simb::MCParticle &genie_part,
593  const simb::MCTruth &mctruth,
594  const std::vector<art::Ptr<simb::MCParticle>> &g4_mcparticles,
595  const std::vector<const sim::GeneratedParticleInfo *> infos) {
596 
597  // only stable final state particles are propogated by G4
598  if (genie_part.StatusCode() != 1) return NULL;
599 
600  const simb::MCParticle *ret = NULL;
601  for (int iparticle = 0; iparticle < (int)g4_mcparticles.size(); iparticle++) {
602  if (infos[iparticle]->hasGeneratedParticleIndex() &&
603  (int)infos[iparticle]->generatedParticleIndex() < mctruth.NParticles() && // TODO: why is this number sometimes bigger than the number of particles?
604  mctruth.GetParticle(infos[iparticle]->generatedParticleIndex()).TrackId() == genie_part.TrackId() &&
605  g4_mcparticles[iparticle]->Process() == "primary" /* TODO: will have to remove this restriction to include secondary particles*/) {
606 
607  // if a genie particle re-scatters in g4 and makes more particles, then multiple g4 particles can match to a
608  // genie particle. Thus, we also check that the start location of the associated genie particle matches the g4
609  // and that the pdgid matches (to be on the safe side)
610  //
611  // Note that this should be accounted for by requiring the process to be primary. This is a bit of redundancy.
612  const simb::MCParticle& matched_genie_particle = mctruth.GetParticle(infos[iparticle]->generatedParticleIndex());
613  if ((matched_genie_particle.Position().Vect() - g4_mcparticles[iparticle]->Position().Vect()).Mag() < 1e-4 &&
614  matched_genie_particle.PdgCode() == g4_mcparticles[iparticle]->PdgCode()) {
615 
616  // this should only be true for one particle
617  assert(ret == NULL);
618  ret = g4_mcparticles[iparticle].get();
619  }
620  }
621  }
622  return ret;
623 }
624 
process_name vertex
Definition: cheaterreco.fcl:51
void FillMatchingSlc(Histos &h, float purity, float completeness)
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
Algorithm to compute various geometrical relation among geometrical objects. In particular functions ...
void analyze(art::Event const &e) override
Utilities related to art service access.
double ContainedLength(const TVector3 &v0, const TVector3 &v1, const std::vector< geo::BoxBoundedGeo > &boxes)
Definition: Interaction.cxx:28
std::vector< geo::BoxBoundedGeo > fActiveVolumes
ClusterModuleLabel join with tracks
const geo::GeometryCore * geometry
auto const tol
Definition: SurfXYZTest.cc:16
double SqDist(const Line_t &line, const Point_t &pt) const
Declaration of signal hit object.
pdgs p
Definition: selectors.fcl:22
Representation of a simple 3D line segment Defines a finite 3D straight line by having the start and ...
Geometry information for a single TPC.
Definition: TPCGeo.h:38
void InitHisto(Histos &h, const std::string &prefix)
geo::BoxBoundedGeo const & ActiveBoundingBox() const
Returns the box of the active volume of this TPC.
Definition: TPCGeo.h:320
std::vector< geo::BoxBoundedGeo > fFiducialVolumes
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
std::vector< std::vector< geo::BoxBoundedGeo > > fTPCVolumes
Definition of vertex object for LArSoft.
Definition: Vertex.h:35
BEGIN_PROLOG TPC
TPC_iterator begin_TPC() const
Returns an iterator pointing to the first TPC in the detector.
def InFV
Definition: selection.py:13
process_name gaushit a
while getopts h
std::vector< std::pair< int, float > > AllTrueParticleIDEnergyMatches(const detinfo::DetectorClocksData &clockData, const std::vector< art::Ptr< recob::Hit > > &hits, bool rollup_unsaved_ids=1)
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
Access the description of detector geometry.
T abs(T value)
InitHistos()
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
Metadata associated to PFParticles.
Ionization at a point of the TPC sensitive volume.
Definition: SimChannel.h:86
float ParticleLength(const simb::MCParticle &particle)
const PropertiesMap & GetPropertiesMap() const
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
void FillMatchingMu(Histos &h, float purity, float completeness)
j template void())
Definition: json.hpp:3108
Description of geometry of one entire detector.
Declaration of cluster object.
Provides recob::Track data product.
Provides a base class aware of world box coordinates.
void FillTrue(Histos &h, const simb::MCTruth &interaction, const simb::MCParticle &G4lepton)
IteratorBox< cryostat_iterator,&GeometryCore::begin_cryostat,&GeometryCore::end_cryostat > IterateCryostats() const
Enables ranged-for loops on all cryostats of the detector.
const simb::MCParticle * Genie2G4MCParticle(const simb::MCParticle &genie_part, const simb::MCTruth &mctruth, const std::vector< art::Ptr< simb::MCParticle >> &g4_mcparticles, const std::vector< const sim::GeneratedParticleInfo * > infos)
A base class aware of world box coordinatesAn object describing a simple shape can inherit from this ...
Definition: BoxBoundedGeo.h:33
do i e
then echo fcl name
art::ServiceHandle< art::TFileService > tfs
pdgs k
Definition: selectors.fcl:22
std::vector< Point_t > Intersection(const AABox_t &box, const HalfLine_t &line, bool back=false) const
Intersection between a HalfLine and an AABox.
Forward iterator browsing all geometry elements in the detector.
Definition: GeometryCore.h:727
NuMuEfficiencyStudy & operator=(NuMuEfficiencyStudy const &)=delete
const Point_t & position() const
Return vertex 3D position.
Definition: Vertex.h:60
physics associatedGroupsWithLeft p1
art framework interface to geometry description
BEGIN_PROLOG could also be cout
TPC_iterator end_TPC() const
Returns an iterator pointing after the last TPC in the detector.
NuMuEfficiencyStudy(fhicl::ParameterSet const &p)