All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MuPVertexStudy_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 // Class: MuPVertexStudy
3 // Plugin Type: analyzer (art v3_03_01)
4 // File: MuPVertexStudy_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 namespace numu {
58  class MuPVertexStudy;
59 }
60 
61 struct TrackHistos {
63  TH1D *muon_length;
67  TH1D *muon_purity;
69 };
70 
71 struct VertexHistos {
72  TH1D *vertex_diff;
73 };
74 
75 struct TrueHistos {
77  TH1D *muon_length;
79 };
80 
81 float Completion(const simb::MCParticle &particle, float particleE, const std::vector<std::pair<int, float>> &matches, const std::vector<simb::MCParticle> &particles);
82 float Purity(const simb::MCParticle &particle, float totalE, const std::vector<std::pair<int, float>> &matches, const std::vector<simb::MCParticle> &particles);
83 float ContainedLength(const TVector3 &v0, const TVector3 &v1,
84  const std::vector<geoalgo::AABox> &boxes);
85 
86 class numu::MuPVertexStudy : public art::EDAnalyzer {
87 public:
88  explicit MuPVertexStudy(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.
93  MuPVertexStudy(MuPVertexStudy const&) = delete;
94  MuPVertexStudy(MuPVertexStudy&&) = delete;
95  MuPVertexStudy& operator=(MuPVertexStudy const&) = delete;
97 
98  // Required functions.
99  void analyze(art::Event const& e) override;
100 
101 private:
102  //TH1D *nPFParticle;
104  std::array<TrackHistos, 3> fTrackHistos;
106  std::vector<std::vector<geo::BoxBoundedGeo>> fTPCVolumes;
107  std::vector<geo::BoxBoundedGeo> fActiveVolumes;
108 
109  void InitTrue();
110  void InitReco(unsigned i, const std::string &prefix);
111  void InitVertex();
112 
113  void FillReco(unsigned i, const simb::MCParticle &muon, const simb::MCParticle &proton, float muon_completion, float proton_completion, float muon_purity, float proton_purity);
114  void FillVertex(const TVector3 vertex, const recob::Track *muon, const recob::Track *proton);
115 
116  void FillTrue(const simb::MCParticle &muon, const simb::MCParticle &proton);
117  float ParticleLength(const simb::MCParticle &particle);
118 
119 };
120 
122  art::ServiceHandle<art::TFileService> tfs;
123 
124  fTrueHistos.cos_open_angle = tfs->make<TH1D>("CosOpenAngle", "", 64, -3.2, 3.2);
125  fTrueHistos.muon_length = tfs->make<TH1D>("mu_length", "", 100, 0., 500.);
126  fTrueHistos.proton_length = tfs->make<TH1D>("p_length", "", 100, 0., 200.);
127 }
128 
129 void numu::MuPVertexStudy::InitReco(unsigned i, const std::string &prefix) {
130  art::ServiceHandle<art::TFileService> tfs;
131 
132  fTrackHistos[i].cos_open_angle = tfs->make<TH1D>((prefix + "reco_CosOpenAngle").c_str(), "", 64, -3.2, 3.2);
133  fTrackHistos[i].muon_length = tfs->make<TH1D>((prefix + "reco_mu_length").c_str(), "", 100, 0., 500.);
134  fTrackHistos[i].proton_length = tfs->make<TH1D>((prefix + "reco_p_length").c_str(), "", 100, 0., 200.);
135 
136  fTrackHistos[i].muon_completion = tfs->make<TH1D>((prefix + "reco_mu_completion").c_str(), "", 100, 0., 1.);
137  fTrackHistos[i].proton_completion = tfs->make<TH1D>((prefix + "reco_p_completion").c_str(), "", 100, 0., 1.);
138  fTrackHistos[i].muon_purity = tfs->make<TH1D>((prefix + "reco_mu_purity").c_str(), "", 100, 0., 1.);
139  fTrackHistos[i].proton_purity = tfs->make<TH1D>((prefix + "reco_p_purity").c_str(), "", 100, 0., 1.);
140 }
141 
143  art::ServiceHandle<art::TFileService> tfs;
144 
145  fVertexHistos.vertex_diff = tfs->make<TH1D>("vertex_diff", "", 200, 0, 100.);
146 }
147 
148 numu::MuPVertexStudy::MuPVertexStudy(fhicl::ParameterSet const& p)
149  : EDAnalyzer{p} // ,
150  // More initializers here.
151 {
152 
153  InitTrue();
154  InitVertex();
155 
156  std::vector<std::string> prefixes {"both", "mu", "p"};
157  for (unsigned i = 0; i < 3; i++) {
158  InitReco(i, prefixes[i]);
159  }
160 
161  const geo::GeometryCore *geometry = lar::providerFrom<geo::Geometry>();
162 
163  // first the TPC volumes
164  for (auto const &cryo: geometry->IterateCryostats()) {
165  geo::GeometryCore::TPC_iterator iTPC = geometry->begin_TPC(cryo.ID()),
166  tend = geometry->end_TPC(cryo.ID());
167  std::vector<geo::BoxBoundedGeo> this_tpc_volumes;
168  while (iTPC != tend) {
169  geo::TPCGeo const& TPC = *iTPC;
170  this_tpc_volumes.push_back(TPC.ActiveBoundingBox());
171  iTPC++;
172  }
173  fTPCVolumes.push_back(std::move(this_tpc_volumes));
174  }
175 
176  // then combine them into active volumes
177  for (const std::vector<geo::BoxBoundedGeo> &tpcs: fTPCVolumes) {
178  double XMin = std::min_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MinX() < rhs.MinX(); })->MinX();
179  double YMin = std::min_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MinY() < rhs.MinY(); })->MinY();
180  double ZMin = std::min_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MinZ() < rhs.MinZ(); })->MinZ();
181 
182  double XMax = std::max_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MaxX() < rhs.MaxX(); })->MaxX();
183  double YMax = std::max_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MaxY() < rhs.MaxY(); })->MaxY();
184  double ZMax = std::max_element(tpcs.begin(), tpcs.end(), [](auto &lhs, auto &rhs) { return lhs.MaxZ() < rhs.MaxZ(); })->MaxZ();
185 
186  fActiveVolumes.emplace_back(XMin, XMax, YMin, YMax, ZMin, ZMax);
187  }
188 
189 }
190 
191 float numu::MuPVertexStudy::ParticleLength(const simb::MCParticle &particle) {
192  std::vector<geoalgo::AABox> aa_volumes;
193  for (const geo::BoxBoundedGeo &AV: fActiveVolumes) {
194  if (AV.ContainsPosition(particle.Position().Vect())) {
195  aa_volumes.emplace_back(AV.MinX(), AV.MinY(), AV.MinZ(), AV.MaxX(), AV.MaxY(), AV.MaxZ());
196  break;
197  }
198  }
199 
200  float length = 0.;
201  for (unsigned i = 1; i < particle.NumberTrajectoryPoints(); i++) {
202  length += ContainedLength(particle.Position(i-1).Vect(), particle.Position(i).Vect(), aa_volumes);
203  }
204 
205  return length;
206 }
207 
208 void numu::MuPVertexStudy::FillTrue(const simb::MCParticle &muon, const simb::MCParticle &proton) {
209  fTrueHistos.muon_length->Fill(ParticleLength(muon));
210  fTrueHistos.proton_length->Fill(ParticleLength(proton));
211 
212  fTrueHistos.cos_open_angle->Fill(muon.Momentum().Vect().Dot(proton.Momentum().Vect()) / (muon.Momentum().Vect().Mag() * proton.Momentum().Vect().Mag()));
213 }
214 
215 void numu::MuPVertexStudy::FillReco(unsigned i, const simb::MCParticle &muon, const simb::MCParticle &proton, float muon_completion, float proton_completion, float muon_purity, float proton_purity) {
216 
217  fTrackHistos[i].muon_completion->Fill(muon_completion);
218  fTrackHistos[i].proton_completion->Fill(proton_completion);
219  fTrackHistos[i].muon_purity->Fill(muon_purity);
220  fTrackHistos[i].proton_purity->Fill(proton_purity);
221 
222  fTrackHistos[i].muon_length->Fill(ParticleLength(muon));
223  fTrackHistos[i].proton_length->Fill(ParticleLength(proton));
224 
225  fTrackHistos[i].cos_open_angle->Fill(muon.Momentum().Vect().Dot(proton.Momentum().Vect()) / (muon.Momentum().Vect().Mag() * proton.Momentum().Vect().Mag()));
226 
227 }
228 
229 void numu::MuPVertexStudy::FillVertex(const TVector3 vertex, const recob::Track *muon, const recob::Track *proton) {
230  TVector3 muon_start(muon->Start().X(), muon->Start().Y(), muon->Start().Z());
231  TVector3 muon_end(muon->End().X(), muon->End().Y(), muon->End().Z());
232  fVertexHistos.vertex_diff->Fill(std::min((vertex - muon_start).Mag(), (vertex - muon_end).Mag()));
233 }
234 
235 void numu::MuPVertexStudy::analyze(art::Event const& ev)
236 {
237  auto const clock_data = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(ev);
238  art::ServiceHandle<cheat::BackTrackerService> backtracker;
239 
240  const std::vector<simb::MCParticle> &mcparticle_list = *ev.getValidHandle<std::vector<simb::MCParticle>>("largeant");
241 
242  const simb::MCParticle *muon = NULL;
243  const simb::MCParticle *proton = NULL;
244 
245  // get the muon and proton
246  for (unsigned i = 0; i < mcparticle_list.size(); i++) {
247  if (mcparticle_list[i].PdgCode() == 13 && mcparticle_list[i].Process() == "primary") {
248  assert(muon == NULL);
249  muon = &mcparticle_list[i];
250  }
251  if (mcparticle_list[i].PdgCode() == 2212 && mcparticle_list[i].Process() == "primary") {
252  assert(proton == NULL);
253  proton = &mcparticle_list[i];
254  }
255  }
256 
257  assert(muon != NULL);
258  assert(proton != NULL);
259 
260  FillTrue(*muon, *proton);
261 
262  float muonE = 0.;
263  std::vector<const sim::IDE*> muon_ides(backtracker->TrackIdToSimIDEs_Ps(muon->TrackId()));
264  for (const sim::IDE *ide: muon_ides) muonE += ide->energy;
265 
266  float protonE = 0.;
267  std::vector<const sim::IDE*> proton_ides(backtracker->TrackIdToSimIDEs_Ps(proton->TrackId()));
268  for (const sim::IDE *ide: proton_ides) protonE += ide->energy;
269 
270  const auto &particle_handle = ev.getValidHandle<std::vector<recob::PFParticle>>("pandora");
271  const std::vector<recob::PFParticle> &particle_list = *particle_handle;
272  art::FindManyP<recob::Cluster> particles_to_clusters(particle_handle, ev, "pandora");
273  art::FindManyP<recob::Track> particles_to_tracks(particle_handle, ev, "pandoraTrack");
274 
275  const recob::Track *muon_track = NULL;
276  const recob::Track *proton_track = NULL;
277 
278  for (unsigned i_part = 0; i_part < particle_list.size(); i_part++) {
279  const std::vector<art::Ptr<recob::Cluster>> clusters = particles_to_clusters.at(i_part);
280  art::FindManyP<recob::Hit> clusters_to_hits(clusters, ev, "pandora");
281 
282  std::vector<art::Ptr<recob::Hit>> hits;
283  for (unsigned i_clus = 0; i_clus < clusters.size(); i_clus++) {
284  const std::vector<art::Ptr<recob::Hit>> &this_hits = clusters_to_hits.at(i_clus);
285  hits.insert(hits.end(), this_hits.begin(), this_hits.end());
286  }
287 
288  std::vector<std::pair<int, float>> matches = CAFRecoUtils::AllTrueParticleIDEnergyMatches(clock_data, hits);
289  float totalE = CAFRecoUtils::TotalHitEnergy(clock_data, hits);
290 
291  float muon_completion = Completion(*muon, muonE, matches, mcparticle_list);
292  float proton_completion = Completion(*proton, protonE, matches, mcparticle_list);
293 
294  float muon_purity = Purity(*muon, totalE, matches, mcparticle_list);
295  float proton_purity = Purity(*proton, totalE, matches, mcparticle_list);
296 
297  const recob::Track *track = particles_to_tracks.at(i_part).size() ? particles_to_tracks.at(i_part).at(0).get() : NULL;
298 
299  if (muon_completion > 0.5 && proton_completion > 0.5) {
300  FillReco(0, *muon, *proton, muon_completion, proton_completion, muon_purity, proton_purity);
301  muon_track = track;
302  proton_track = track;
303  }
304  else if (muon_completion > 0.5) {
305  FillReco(1, *muon, *proton, muon_completion, proton_completion, muon_purity, proton_purity);
306  muon_track = track;
307  }
308  else if (proton_completion > 0.5) {
309  FillReco(2, *muon, *proton, muon_completion, proton_completion, muon_purity, proton_purity);
310  proton_track = track;
311  }
312  }
313  if (muon_track && proton_track && muon_track != proton_track) {
314  FillVertex(muon->Position().Vect(), muon_track, proton_track);
315  }
316 }
317 
318 float Completion(const simb::MCParticle &particle, float particleE, const std::vector<std::pair<int, float>> &matches, const std::vector<simb::MCParticle> &particles) {
319  for (auto const &pair: matches) {
320  if (pair.first == particle.TrackId()) {
321  return pair.second / particleE;
322  }
323  }
324  return 0.;
325 }
326 
327 float Purity(const simb::MCParticle &particle, float totalE, const std::vector<std::pair<int, float>> &matches, const std::vector<simb::MCParticle> &particles) {
328  float matchE = 0.;
329  for (auto const &pair: matches) {
330  bool matches = false;
331  if (pair.first == particle.TrackId()) matches = true;
332  else {
333  for (int i_d = 0; i_d < particle.NumberDaughters(); i_d++) {
334  if (pair.first == particle.Daughter(i_d)) {
335  bool showerDaughter = true;
336  for (unsigned i_p = 0; i_p < particles.size(); i_p++) {
337  if (pair.first == particles[i_p].TrackId()) {
338  showerDaughter = particles[i_p].Process() == "muIoni";
339  break;
340  }
341  }
342  matches = showerDaughter;
343  break;
344  }
345  }
346  }
347  if (matches) matchE += pair.second;
348  }
349  return matchE / totalE;
350 }
351 
352 float ContainedLength(const TVector3 &v0, const TVector3 &v1,
353  const std::vector<geoalgo::AABox> &boxes) {
354  static const geoalgo::GeoAlgo algo;
355 
356  // if points are the same, return 0
357  if ((v0 - v1).Mag() < 1e-6) return 0;
358 
359  // construct individual points
360  geoalgo::Point_t p0(v0);
361  geoalgo::Point_t p1(v1);
362 
363  // construct line segment
364  geoalgo::LineSegment line(p0, p1);
365 
366  double length = 0;
367 
368  // total contained length is sum of lengths in all boxes
369  // assuming they are non-overlapping
370  for (auto const &box: boxes) {
371  int n_contained = box.Contain(p0) + box.Contain(p1);
372  // both points contained -- length is total length (also can break out of loop)
373  if (n_contained == 2) {
374  length = (v1 - v0).Mag();
375  break;
376  }
377  // one contained -- have to find intersection point (which must exist)
378  if (n_contained == 1) {
379  auto intersections = algo.Intersection(line, box);
380  // Because of floating point errors, it can sometimes happen
381  // that there is 1 contained point but no "Intersections"
382  // if one of the points is right on the edge
383  if (intersections.size() == 0) {
384  // determine which point is on the edge
385  double tol = 1e-5;
386  bool p0_edge = algo.SqDist(p0, box) < tol;
387  bool p1_edge = algo.SqDist(p1, box) < tol;
388  assert(p0_edge || p1_edge);
389  // contained one is on edge -- can treat both as not contained
390  //
391  // In this case, no length
392  if ((p0_edge && box.Contain(p0)) || (box.Contain(p1) && p1_edge))
393  continue;
394  // un-contaned one is on edge -- treat both as contained
395  else if ((p0_edge && box.Contain(p1)) || (box.Contain(p0) && p1_edge)) {
396  length = (v1 - v0).Mag();
397  break;
398  }
399  else {
400  assert(false); // bad
401  }
402  }
403  // floating point errors can also falsely cause 2 intersection points
404  //
405  // in this case, one of the intersections must be very close to the
406  // "contained" point, so the total contained length will be about
407  // the same as the distance between the two intersection points
408  else if (intersections.size() == 2) {
409  length += (intersections.at(0).ToTLorentzVector().Vect() - intersections.at(1).ToTLorentzVector().Vect()).Mag();
410  continue;
411  }
412  // "Correct"/ideal case -- 1 intersection point
413  else if (intersections.size() == 1) {
414  // get TVector at intersection point
415  TVector3 int_tv(intersections.at(0).ToTLorentzVector().Vect());
416  length += ( box.Contain(p0) ? (v0 - int_tv).Mag() : (v1 - int_tv).Mag() );
417  }
418  else assert(false); // bad
419  }
420  // none contained -- either must have zero or two intersections
421  if (n_contained == 0) {
422  auto intersections = algo.Intersection(line, box);
423  if (!(intersections.size() == 0 || intersections.size() == 2)) {
424  // more floating point error fixes...
425  //
426  // figure out which points are near the edge
427  double tol = 1e-5;
428  bool p0_edge = algo.SqDist(p0, box) < tol;
429  bool p1_edge = algo.SqDist(p1, box) < tol;
430  // and which points are near the intersection
431  TVector3 vint = intersections.at(0).ToTLorentzVector().Vect();
432 
433  bool p0_int = (v0 - vint).Mag() < tol;
434  bool p1_int = (v1 - vint).Mag() < tol;
435  // exactly one of them should produce the intersection
436  assert((p0_int && p0_edge) != (p1_int && p1_edge));
437  // void variables when assert-ions are turned off
438  (void) p0_int; (void) p1_int;
439 
440  // both close to edge -- full length is contained
441  if (p0_edge && p1_edge) {
442  length += (v0 - v1).Mag();
443  }
444  // otherwise -- one of them is not on an edge, no length is contained
445  else {}
446  }
447  // assert(intersections.size() == 0 || intersections.size() == 2);
448  else if (intersections.size() == 2) {
449  TVector3 start(intersections.at(0).ToTLorentzVector().Vect());
450  TVector3 end(intersections.at(1).ToTLorentzVector().Vect());
451  length += (start - end).Mag();
452  }
453  }
454  }
455 
456  return length;
457 }//ContainedLength
458 
459 DEFINE_ART_MODULE(numu::MuPVertexStudy)
process_name vertex
Definition: cheaterreco.fcl:51
Algorithm to compute various geometrical relation among geometrical objects. In particular functions ...
void InitReco(unsigned i, const std::string &prefix)
Utilities related to art service access.
double ContainedLength(const TVector3 &v0, const TVector3 &v1, const std::vector< geo::BoxBoundedGeo > &boxes)
Definition: Interaction.cxx:28
void FillTrue(const simb::MCParticle &muon, const simb::MCParticle &proton)
const geo::GeometryCore * geometry
void FillReco(unsigned i, const simb::MCParticle &muon, const simb::MCParticle &proton, float muon_completion, float proton_completion, float muon_purity, float proton_purity)
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
MuPVertexStudy(fhicl::ParameterSet const &p)
std::vector< geo::BoxBoundedGeo > fActiveVolumes
MuPVertexStudy & operator=(MuPVertexStudy const &)=delete
geo::BoxBoundedGeo const & ActiveBoundingBox() const
Returns the box of the active volume of this TPC.
Definition: TPCGeo.h:320
process_name use argoneut_mc_hitfinder track
BEGIN_PROLOG TPC
TPC_iterator begin_TPC() const
Returns an iterator pointing to the first TPC in the detector.
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.
Point_t const & Start() const
Access to track position at different points.
Ionization at a point of the TPC sensitive volume.
Definition: SimChannel.h:86
float ParticleLength(const simb::MCParticle &particle)
std::vector< std::vector< geo::BoxBoundedGeo > > fTPCVolumes
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
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.
IteratorBox< cryostat_iterator,&GeometryCore::begin_cryostat,&GeometryCore::end_cryostat > IterateCryostats() const
Enables ranged-for loops on all cryostats of the detector.
process_name can override from command line with o or output muon
Definition: runPID.fcl:28
A base class aware of world box coordinatesAn object describing a simple shape can inherit from this ...
Definition: BoxBoundedGeo.h:33
void analyze(art::Event const &e) override
do i e
Point_t const & End() const
std::array< TrackHistos, 3 > fTrackHistos
float Completion(const simb::MCParticle &particle, float particleE, const std::vector< std::pair< int, float >> &matches, const std::vector< simb::MCParticle > &particles)
float Purity(const simb::MCParticle &particle, float totalE, const std::vector< std::pair< int, float >> &matches, const std::vector< simb::MCParticle > &particles)
void FillVertex(const TVector3 vertex, const recob::Track *muon, const recob::Track *proton)
float TotalHitEnergy(const detinfo::DetectorClocksData &clockData, const std::vector< art::Ptr< recob::Hit > > &hits)
art::ServiceHandle< art::TFileService > tfs
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
physics associatedGroupsWithLeft p1
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 &quot;fitted&quot; track:
TPC_iterator end_TPC() const
Returns an iterator pointing after the last TPC in the detector.