LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
CosmicPCAxisTagger_module.cc
Go to the documentation of this file.
1 // Class: CosmicPCAxisTagger
3 // Module Type: producer
4 // File: CosmicPCAxisTagger_module.cc
5 // This module checks timing and TPC volume boundaries as a
6 // way to tag potential cosmic rays
7 // This particular module uses PFParticles as input and handles
8 // special cases associated with them. Instead of tracks, it uses
9 // PCAxis objects for getting the start/end points of the
10 // candidate CR's
11 // This module started life as CosmicTrackTagger_module, written
12 // by Sarah Lockwitz, and small alterations made to handle the
13 // PFParticle input
14 //
15 // Generated at Wed Sep 17 19:17:00 2014 by Tracy Usher by cloning CosmicTrackTagger
16 // from art v1_02_02.
17 // artmod -e beginJob -e reconfigure -e endJob producer trkf::CosmicPCAxisTagger
19 
24 
25 #include <algorithm>
26 #include <cmath>
27 #include <iostream>
28 #include <iterator>
29 
42 
43 #include "TVector3.h"
44 
45 namespace cosmic {
46  class CosmicPCAxisTagger;
47  class SpacePoint;
48 }
49 
51 public:
52  explicit CosmicPCAxisTagger(fhicl::ParameterSet const& p);
53 
54  void produce(art::Event& e) override;
55 
56 private:
57  typedef std::vector<reco::ClusterHit2D> Hit2DVector;
58 
60  std::string fPCAxisModuleLabel;
61 
63 
67 };
68 
70  : EDProducer{p}, fPcaAlg(p.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
71 {
73 
75  fDetWidth = 2. * geo->DetHalfWidth();
76  fDetLength = geo->DetLength();
77 
78  auto const clock_data = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataForJob();
79  float fSamplingRate = sampling_rate(clock_data);
80 
81  fPFParticleModuleLabel = p.get<std::string>("PFParticleModuleLabel");
82  fPCAxisModuleLabel = p.get<std::string>("PCAxisModuleLabel");
83 
84  fTPCXBoundary = p.get<float>("TPCXBoundary", 5);
85  fTPCYBoundary = p.get<float>("TPCYBoundary", 5);
86  fTPCZBoundary = p.get<float>("TPCZBoundary", 5);
87 
88  auto const detector =
90  const double driftVelocity =
91  detector.DriftVelocity(detector.Efield(), detector.Temperature()); // cm/us
92 
94  2 * geo->DetHalfWidth() / (driftVelocity * fSamplingRate / 1000); // ~3200 for uB
95 
96  produces<std::vector<anab::CosmicTag>>();
97  produces<art::Assns<recob::PFParticle, anab::CosmicTag>>();
98  produces<art::Assns<recob::PCAxis, anab::CosmicTag>>();
99 }
100 
102 {
103  // Instatiate the output
104  std::unique_ptr<std::vector<anab::CosmicTag>> cosmicTagPFParticleVector(
105  new std::vector<anab::CosmicTag>);
106  std::unique_ptr<art::Assns<recob::PCAxis, anab::CosmicTag>> assnOutCosmicTagPCAxis(
108  std::unique_ptr<art::Assns<recob::PFParticle, anab::CosmicTag>> assnOutCosmicTagPFParticle(
110 
111  // Recover handle for PFParticles
113  evt.getByLabel(fPFParticleModuleLabel, pfParticleHandle);
114 
115  if (!pfParticleHandle.isValid()) {
116  evt.put(std::move(cosmicTagPFParticleVector));
117  evt.put(std::move(assnOutCosmicTagPFParticle));
118  evt.put(std::move(assnOutCosmicTagPCAxis));
119  return;
120  }
121 
122  // We need a handle to the collection of clusters in the data store so we can
123  // handle associations to hits.
125  evt.getByLabel(fPFParticleModuleLabel, clusterHandle);
126 
127  // Recover the handle for the PCAxes
129  evt.getByLabel(fPCAxisModuleLabel, pcaxisHandle);
130 
131  if (!pcaxisHandle.isValid() || !clusterHandle.isValid()) {
132  evt.put(std::move(cosmicTagPFParticleVector));
133  evt.put(std::move(assnOutCosmicTagPFParticle));
134  evt.put(std::move(assnOutCosmicTagPCAxis));
135  return;
136  }
137 
138  // Recover the list of associated PCA axes
139  art::FindManyP<recob::PCAxis> pfPartToPCAxisAssns(pfParticleHandle, evt, fPCAxisModuleLabel);
140 
141  // Add the relations to recover associations cluster hits
142  art::FindManyP<recob::SpacePoint> spacePointAssnVec(
143  pfParticleHandle, evt, fPFParticleModuleLabel);
144 
145  // Recover the collection of associations between PFParticles and clusters, this will
146  // be the mechanism by which we actually deal with clusters
147  art::FindManyP<recob::Cluster> clusterAssns(pfParticleHandle, evt, fPFParticleModuleLabel);
148 
149  // Likewise, recover the collection of associations to hits
150  art::FindManyP<recob::Hit> clusterHitAssns(clusterHandle, evt, fPFParticleModuleLabel);
151 
152  // The outer loop is going to be over PFParticles
153  for (size_t pfPartIdx = 0; pfPartIdx != pfParticleHandle->size(); pfPartIdx++) {
154  art::Ptr<recob::PFParticle> pfParticle(pfParticleHandle, pfPartIdx);
155 
156  // Recover the PCAxis vector
157  std::vector<art::Ptr<recob::PCAxis>> pcAxisVec = pfPartToPCAxisAssns.at(pfPartIdx);
158 
159  // Is there an axis associated to this PFParticle?
160  if (pcAxisVec.empty()) continue;
161 
162  // *****************************************************************************************
163  // For what follows below we want the "best" PCAxis object only. However, it can be that
164  // there are two PCAxes for a PFParticle (depending on source) where the "first" axis will
165  // be the "better" one that we want (this statement by fiat, it is defined that way in the
166  // axis producer module).
167  if (pcAxisVec.size() > 1 && pcAxisVec.front()->getID() > pcAxisVec.back()->getID())
168  std::reverse(pcAxisVec.begin(), pcAxisVec.end());
169  // We need to confirm this!!
170  // *****************************************************************************************
171 
172  // Recover the axis
173  const art::Ptr<recob::PCAxis>& pcAxis = pcAxisVec.front();
174 
175  // Start the tagging process...
176  int isCosmic = 0;
178 
179  // There are two sections to the tagging, in the first we are going to check for hits that are
180  // "out of time" and for this we only need the hit vector. If no hits are out of time then
181  // we need to do a more thorough check of the positions of the hits.
182  // If we find hits that are out of time we'll set the "end points" of our trajectory to
183  // a scale factor past the principle eigen value
184  double eigenVal0 = sqrt(pcAxis->getEigenValues()[0]);
185  double maxArcLen = 3. * eigenVal0;
186 
187  // Recover PCA end points
188  TVector3 vertexPosition(
189  pcAxis->getAvePosition()[0], pcAxis->getAvePosition()[1], pcAxis->getAvePosition()[2]);
190  TVector3 vertexDirection(pcAxis->getEigenVectors()[0][0],
191  pcAxis->getEigenVectors()[0][1],
192  pcAxis->getEigenVectors()[0][2]);
193 
194  TVector3 pcAxisStart = vertexPosition - maxArcLen * vertexDirection;
195  TVector3 pcAxisEnd = vertexPosition + maxArcLen * vertexDirection;
196 
197  // "Track" end points in easily readable form
198  float trackEndPt1_X = pcAxisStart[0];
199  float trackEndPt1_Y = pcAxisStart[1];
200  float trackEndPt1_Z = pcAxisStart[2];
201  float trackEndPt2_X = pcAxisEnd[0];
202  float trackEndPt2_Y = pcAxisEnd[1];
203  float trackEndPt2_Z = pcAxisEnd[2];
204 
205  // Now we get the 2D clusters associated to this PFParticle
206  std::vector<art::Ptr<recob::Cluster>> clusterVec = clusterAssns.at(pfParticle.key());
207 
208  bool dumpMe(false);
209 
210  // Once we have the clusters then we can loop over them to find the associated hits
211  for (const auto& cluster : clusterVec) {
212  // Recover the 2D hits associated to a given cluster
213  std::vector<art::Ptr<recob::Hit>> hitVec = clusterHitAssns.at(cluster->ID());
214 
215  // Once we have the hits the first thing we should do is to check if any are "out of time"
216  // If there are out of time hits then we are going to reject the cluster so no need to do
217  // any further processing.
219  // Check that all hits on particle are "in time"
221  for (const auto& hit : hitVec) {
222  if (dumpMe) {
223  std::cout << "***>> Hit key: " << hit.key() << ", peak - RMS: " << hit->PeakTimeMinusRMS()
224  << ", peak + RMS: " << hit->PeakTimePlusRMS()
225  << ", det width: " << fDetectorWidthTicks << std::endl;
226  }
227  if (hit->PeakTimeMinusRMS() < fDetectorWidthTicks ||
228  hit->PeakTimePlusRMS() > 2. * fDetectorWidthTicks) {
229  isCosmic = 1;
231  break; // If one hit is out of time it must be a cosmic ray
232  }
233  }
234  }
235 
236  // Recover the space points associated to this PFParticle.
237  std::vector<art::Ptr<recob::SpacePoint>> spacePointVec = spacePointAssnVec.at(pfParticle.key());
238 
240  // Now check the TPC boundaries:
242  if (isCosmic == 0 && !spacePointVec.empty()) {
243  // Do a check on the transverse components of the PCA axes to make sure we are looking at long straight
244  // tracks and not the kind of events we might want to keep
245  double transRMS =
246  sqrt(std::pow(pcAxis->getEigenValues()[1], 2) + std::pow(pcAxis->getEigenValues()[1], 2));
247 
248  if (eigenVal0 > 0. && transRMS > 0.) {
249  // The idea is to find the maximum extents of this PFParticle using the PCA axis which we
250  // can then use to determine proximity to a TPC boundary.
251  // We implement this by recovering the 3D Space Points and then make a pass through them to
252  // find the space points at the extremes of the distance along the principle axis.
253  // We'll loop through the space points looking for those which have the largest arc lengths along
254  // the principle axis. Set up to do that
255  double arcLengthToFirstHit(9999.);
256  double arcLengthToLastHit(-9999.);
257 
258  for (const auto spacePoint : spacePointVec) {
259  TVector3 spacePointPos(spacePoint->XYZ()[0], spacePoint->XYZ()[1], spacePoint->XYZ()[2]);
260  TVector3 deltaPos = spacePointPos - vertexPosition;
261  double arcLenToHit = deltaPos.Dot(vertexDirection);
262 
263  if (arcLenToHit < arcLengthToFirstHit) {
264  arcLengthToFirstHit = arcLenToHit;
265  pcAxisStart = spacePointPos;
266  }
267 
268  if (arcLenToHit > arcLengthToLastHit) {
269  arcLengthToLastHit = arcLenToHit;
270  pcAxisEnd = spacePointPos;
271  }
272  }
273 
274  // "Track" end points in easily readable form
275  trackEndPt1_X = pcAxisStart[0];
276  trackEndPt1_Y = pcAxisStart[1];
277  trackEndPt1_Z = pcAxisStart[2];
278  trackEndPt2_X = pcAxisEnd[0];
279  trackEndPt2_Y = pcAxisEnd[1];
280  trackEndPt2_Z = pcAxisEnd[2];
281 
282  // In below we check entry and exit points. Note that a special case of a particle entering
283  // and exiting the same surface is considered to be running parallel to the surface and NOT
284  // entering and exiting.
285  // Also, in what follows we make no assumptions on which end point is the "start" or
286  // "end" of the track being considered.
287  bool nBdX[] = {false, false};
288  bool nBdY[] = {false, false};
289  bool nBdZ[] = {false, false};
290 
291  // Check x extents - note that uboone coordinaes system has x=0 at edge
292  // Note this counts the case where the track enters and exits the same surface as a "1", not a "2"
293  // Also note that, in theory, any cosmic ray entering or exiting the X surfaces will have presumably
294  // been removed already by the checking of "out of time" hits... but this will at least label
295  // neutrino interaction tracks which exit through the X surfaces of the TPC
296  if (fDetWidth - trackEndPt1_X < fTPCXBoundary || trackEndPt1_X < fTPCXBoundary)
297  nBdX[0] = true;
298  if (fDetWidth - trackEndPt2_X < fTPCXBoundary || trackEndPt2_X < fTPCXBoundary)
299  nBdX[1] = true;
300 
301  // Check y extents (note coordinate system change)
302  // Note this counts the case where the track enters and exits the same surface as a "1", not a "2"
303  if (fDetHalfHeight - trackEndPt1_Y < fTPCYBoundary ||
304  fDetHalfHeight + trackEndPt1_Y < fTPCYBoundary)
305  nBdY[0] = true; // one end of track exits out top
306  if (fDetHalfHeight - trackEndPt2_Y < fTPCYBoundary ||
307  fDetHalfHeight + trackEndPt2_Y < fTPCYBoundary)
308  nBdY[1] = true; // one end of track exist out bottom
309 
310  // Check z extents
311  // Note this counts the case where the track enters and exits the same surface as a "1", not a "2"
312  if (fDetLength - trackEndPt1_Z < fTPCZBoundary || trackEndPt1_Z < fTPCZBoundary)
313  nBdZ[0] = true;
314  if (fDetLength - trackEndPt2_Z < fTPCZBoundary || trackEndPt2_Z < fTPCZBoundary)
315  nBdZ[1] = true;
316 
317  // Endpoints exiting?
318  bool exitEnd1 = nBdX[0] || nBdY[0]; // end point 1 enters/exits top/bottom or x sides
319  bool exitEnd2 = nBdX[1] || nBdY[1]; // end point 2 enters/exits top/bottom or x sides
320  bool exitEndZ1 =
321  exitEnd1 && nBdZ[1]; // end point 1 enters/exits top/bottom and exits/enters z
322  bool exitEndZ2 =
323  exitEnd1 && nBdZ[0]; // end point 2 enters/exits top/bottom and exits/enters z
324 
325  // This should check for the case of a track which is both entering and exiting
326  // but we consider entering and exiting the z boundaries to be a special case (should it be?)
327  if ((exitEnd1 && exitEnd2) || exitEndZ1 || exitEndZ2) {
328  isCosmic = 2;
329  if (nBdX[0] && nBdX[1])
331  else if (nBdY[0] && nBdY[1])
333  else if ((nBdX[0] || nBdX[1]) && (nBdY[0] || nBdY[1]))
335  else if ((nBdX[0] || nBdX[1]) && (nBdZ[0] || nBdZ[1]))
337  else
339  }
340  // This is the special case of track which appears to enter/exit z boundaries
341  else if (nBdZ[0] && nBdZ[1]) {
342  isCosmic = 3;
344  }
345  // This looks for track which enters/exits a boundary but has other endpoint in TPC
346  else if ((nBdX[0] || nBdY[0] || nBdZ[0]) != (nBdX[1] || nBdY[1] || nBdZ[1])) {
347  isCosmic = 4;
348  if (nBdX[0] || nBdX[1])
350  else if (nBdY[0] || nBdY[1])
352  else if (nBdZ[0] || nBdZ[1])
354  }
355  }
356  }
357 
358  std::vector<float> endPt1;
359  std::vector<float> endPt2;
360  endPt1.push_back(trackEndPt1_X);
361  endPt1.push_back(trackEndPt1_Y);
362  endPt1.push_back(trackEndPt1_Z);
363  endPt2.push_back(trackEndPt2_X);
364  endPt2.push_back(trackEndPt2_Y);
365  endPt2.push_back(trackEndPt2_Z);
366 
367  float cosmicScore = isCosmic > 0 ? 1. : 0.;
368 
369  // Handle special cases
370  if (isCosmic == 3)
371  cosmicScore = 0.4; // Enter/Exit at opposite Z boundaries
372  else if (isCosmic == 4)
373  cosmicScore = 0.5; // Enter or Exit but not both
374 
375  // Create the tag object for this PFParticle and make the corresponding association
376  cosmicTagPFParticleVector->emplace_back(endPt1, endPt2, cosmicScore, tag_id);
377 
378  util::CreateAssn(evt, *cosmicTagPFParticleVector, pfParticle, *assnOutCosmicTagPFParticle);
379 
380  // Loop through the tracks resulting from this PFParticle and mark them
381  for (const auto& axis : pcAxisVec) {
382  util::CreateAssn(evt, *cosmicTagPFParticleVector, axis, *assnOutCosmicTagPCAxis);
383  }
384  }
385 
386  evt.put(std::move(cosmicTagPFParticleVector));
387  evt.put(std::move(assnOutCosmicTagPFParticle));
388  evt.put(std::move(assnOutCosmicTagPCAxis));
389 } // end of produce
390 
const EigenVectors & getEigenVectors() const
Definition: PCAxis.h:74
const double * getEigenValues() const
Definition: PCAxis.h:70
enum anab::cosmic_tag_id CosmicTagID_t
Declaration of signal hit object.
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.cc:6
Length_t DetHalfWidth(TPCID const &tpcid=tpc_zero) const
Returns the half width of the active volume of the specified TPC.
std::vector< reco::ClusterHit2D > Hit2DVector
Cluster finding and building.
Length_t DetLength(TPCID const &tpcid=tpc_zero) const
Returns the length of the active volume of the specified TPC.
bool isValid() const noexcept
Definition: Handle.h:203
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Event.h:77
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:65
CosmicPCAxisTagger(fhicl::ParameterSet const &p)
key_type key() const noexcept
Definition: Ptr.h:166
Declaration of cluster object.
void produce(art::Event &e) override
Detector simulation of raw signals on wires.
This header file defines the interface to a principal components analysis designed to be used within ...
const double * getAvePosition() const
Definition: PCAxis.h:78
bool CreateAssn(art::Event &evt, std::vector< T > const &a, art::Ptr< U > const &b, art::Assns< U, T > &assn, std::string a_instance, size_t index=UINT_MAX)
Creates a single one-to-one association.
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
Utility object to perform functions of association.
lar_cluster3d::PrincipalComponentsAlg fPcaAlg
Principal Components algorithm.
Length_t DetHalfHeight(TPCID const &tpcid=tpc_zero) const
Returns the half height of the active volume of the specified TPC.
TCEvent evt
Definition: DataStructs.cxx:8
Float_t e
Definition: plot.C:35
double sampling_rate(DetectorClocksData const &data)
Returns the period of the TPC readout electronics clock.
Namespace collecting geometry-related classes utilities.
art framework interface to geometry description