All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TrackProxyTest_module.cc
Go to the documentation of this file.
1 /**
2  * @file TrackProxyTest_module.cc
3  * @brief Tests `proxy::Track` class.
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date July 27, 2017
6  *
7  */
8 
9 
10 // LArSoft libraries
11 #include "lardata/RecoBaseProxy/Track.h" // proxy namespace
17 
18 // framework libraries
19 #include "art/Framework/Core/EDAnalyzer.h"
20 #include "art/Framework/Core/ModuleMacros.h"
21 #include "art/Framework/Principal/Event.h"
22 #include "art/Framework/Principal/Handle.h"
23 #include "canvas/Persistency/Common/FindManyP.h"
24 #include "canvas/Persistency/Common/FindOneP.h"
25 #include "canvas/Utilities/InputTag.h"
26 
27 // utility libraries
28 #include "messagefacility/MessageLogger/MessageLogger.h"
29 #include "fhiclcpp/types/Atom.h"
30 #include "fhiclcpp/types/Name.h"
31 #include "fhiclcpp/types/Comment.h"
32 
33 // Boost libraries
34 #include "boost/test/unit_test.hpp"
35 
36 // C/C++ libraries
37 #include <algorithm> // std::for_each()
38 #include <initializer_list>
39 #include <memory> // std::unique_ptr<>
40 #include <cstring> // std::strlen(), std::strcpy()
41 
42 
43 //------------------------------------------------------------------------------
44 /**
45  * @brief Runs a test of `proxy::Tracks` interface.
46  *
47  * This module is that it uses Boost unit test library, and as such it must be
48  * run with `lar_ut` instead of `lar`.
49  */
50 class TrackProxyTest : public art::EDAnalyzer {
51  public:
52 
53  struct Config {
54  using Name = fhicl::Name;
55  using Comment = fhicl::Comment;
56 
57  fhicl::Atom<art::InputTag> tracksTag{
58  Name("tracks"),
59  Comment("tag of the recob::Track data products to run the test on.")
60  };
61 
62  }; // struct Config
63 
64  using Parameters = art::EDAnalyzer::Table<Config>;
65 
66  explicit TrackProxyTest(Parameters const& config)
67  : art::EDAnalyzer(config)
68  , tracksTag(config().tracksTag())
69  {}
70 
71  // Plugins should not be copied or assigned.
72  TrackProxyTest(TrackProxyTest const &) = delete;
73  TrackProxyTest(TrackProxyTest &&) = delete;
74  TrackProxyTest& operator= (TrackProxyTest const &) = delete;
76 
77  virtual void analyze(art::Event const& event) override;
78 
79  private:
80  art::InputTag tracksTag; ///< Tag for the input tracks.
81 
82  /// An example of how to access the information via track proxy.
83  void proxyUsageExample(art::Event const& event) const;
84 
85  /// Returns proxies to tracks longer than a certain length.
86  auto getLongTracks(art::Event const& event, double minLength) const;
87 
88  /// Performs the actual test.
89  void testTracks(art::Event const& event);
90 
91  /// Single-track processing function example.
92  template <typename Track>
93  void processTrack(Track const& track) const;
94 
95  /// Single-track-point processing function example.
96  template <typename TrackPoint>
97  void processPoint(TrackPoint const& point) const;
98 
99 }; // class TrackProxyTest
100 
101 
102 //------------------------------------------------------------------------------
103 template <typename TrackPoint>
104 void TrackProxyTest::processPoint(TrackPoint const& point) const {
105 
106  mf::LogVerbatim log("TrackProxyTest");
107  log <<
108  " [#" << point.index() << "] at " << point.position()
109  << " (momentum: " << point.momentum() << "), flags: "
110  << point.flags();
111 
112  recob::Hit const* hit = point.hit();
113  if (hit) {
114  log << " with a Q=" << hit->Integral() << " hit on channel "
115  << hit->Channel() << " at tick " << hit->PeakTime()
116  << ", measured: " << point.fitInfoPtr()->hitMeas();
117  }
118  else
119  log << " (no associated hit)";
120 
121 } // TrackProxyTest::processPoint()
122 
123 
124 //------------------------------------------------------------------------------
125 template <typename Track>
127 
128  recob::Track const& trackRef = track.track();
129 
130  mf::LogVerbatim("TrackProxyTest")
131  << "[#" << track.index() << "] track " << trackRef
132  << " " << track->Length() << " cm long, with "
133  << trackRef.NPoints() << " points and " << track.nHits()
134  << " hits:";
135 
136  for (auto point: track.points()) {
137  processPoint(point);
138  } // for points in track
139 
140 } // TrackProxyTest::processTrack()
141 
142 
143 //------------------------------------------------------------------------------
144 void TrackProxyTest::proxyUsageExample(art::Event const& event) const {
145 
146  auto tracks = proxy::getCollection<proxy::Tracks>
147  (event, tracksTag, proxy::withFitHitInfo());
148 
149  if (tracks.empty()) {
150  mf::LogVerbatim("TrackProxyTest") << "No tracks in '" << tracksTag.encode()
151  << "'";
152  return;
153  }
154 
155  mf::LogVerbatim("TrackProxyTest") << "Collection '" << tracksTag.encode()
156  << "' contains " << tracks.size() << " tracks.";
157 
158 } // TrackProxyTest::proxyUsageExample()
159 
160 
161 //------------------------------------------------------------------------------
163  (art::Event const& event, double minLength) const
164 {
165  //
166  // this code is not a particularly good practice, but it is aimed to check
167  // that after the proxy collection is out of scope, elements copied from it
168  // are still valid
169  //
170  auto tracks = proxy::getCollection<proxy::Tracks>
171  (event, tracksTag, proxy::withFitHitInfo());
172 
173  std::vector<decltype(tracks)::element_proxy_t> longTracks;
174  for (auto track: tracks) {
175  if (track->Length() >= minLength) longTracks.push_back(track);
176  } // for track
177  return longTracks;
178 
179 } // TrackProxyTest::proxyUsageExample()
180 
181 
182 //------------------------------------------------------------------------------
183 namespace tag {
184  struct SpecialHits {};
185 }
186 
187 void TrackProxyTest::testTracks(art::Event const& event) {
188 
189  auto expectedTracksHandle
190  = event.getValidHandle<std::vector<recob::Track>>(tracksTag);
191  auto const& expectedTracks = *expectedTracksHandle;
192 
193  mf::LogInfo("TrackProxyTest")
194  << "Starting test on " << expectedTracks.size() << " tracks from '"
195  << tracksTag.encode() << "'";
196 
197  art::FindManyP<recob::Hit> hitsPerTrack
198  (expectedTracksHandle, event, tracksTag);
199 
200  art::FindOneP<recob::TrackTrajectory> trajectoryPerTrack
201  (expectedTracksHandle, event, tracksTag);
202 
203  auto const& expectedTrackFitHitInfo
204  = *(event.getValidHandle<std::vector<std::vector<recob::TrackFitHitInfo>>>
205  (tracksTag));
206 
207  auto tracks = proxy::getCollection<proxy::Tracks>(event, tracksTag
208  , proxy::withAssociatedAs<recob::Hit, tag::SpecialHits>()
211  );
212 
213  //
214  // we try to access something we did not "register" in the proxy: space points
215  //
216  static_assert(!tracks.has<recob::SpacePoint>(),
217  "Track proxy does NOT have space points available!!!");
218 
219  static_assert(
221  "recob::TrackFitHitInfo not found!!!"
222  );
223 
224 
225  BOOST_TEST(tracks.empty() == expectedTracks.empty());
226  BOOST_TEST(tracks.size() == expectedTracks.size());
227 
228  BOOST_TEST(tracks.size() == expectedTrackFitHitInfo.size());
229  decltype(auto) allFitHitInfo = tracks.get<recob::TrackFitHitInfo>();
230  static_assert(
231  std::is_lvalue_reference<decltype(allFitHitInfo)>(),
232  "Copy of parallel data!"
233  );
234  BOOST_TEST
235  (allFitHitInfo.data() == std::addressof(expectedTrackFitHitInfo));
236 
237  auto fitHitInfoSize
238  = std::distance(allFitHitInfo.begin(), allFitHitInfo.end());
239  BOOST_TEST(fitHitInfoSize == expectedTrackFitHitInfo.size());
240 
241  std::size_t iExpectedTrack = 0;
242  for (auto const& trackProxy: tracks) {
243  BOOST_TEST_CHECKPOINT("Track #" << trackProxy.index());
244 
245  auto const& expectedTrack = expectedTracks[iExpectedTrack];
246  auto const& expectedHits = hitsPerTrack.at(iExpectedTrack);
247  auto const& expectedFitHitInfo = expectedTrackFitHitInfo[iExpectedTrack];
248  auto const& expectedTrajPtr = trajectoryPerTrack.at(iExpectedTrack);
249  recob::TrackTrajectory const* expectedTrajCPtr
250  = expectedTrajPtr.isNull()? nullptr: expectedTrajPtr.get();
251 
252  recob::Track const& trackRef = *trackProxy;
253 
254  BOOST_TEST
255  (std::addressof(trackRef) == std::addressof(expectedTrack));
256  BOOST_TEST
257  (std::addressof(trackProxy.track()) == std::addressof(expectedTrack));
258  BOOST_TEST(trackProxy.nHits() == expectedHits.size());
259  BOOST_TEST(trackProxy.index() == iExpectedTrack);
260 
261  decltype(auto) fitHitInfo = trackProxy.get<recob::TrackFitHitInfo>();
262  static_assert(
263  std::is_lvalue_reference<decltype(fitHitInfo)>(),
264  "Copy of parallel data element!"
265  );
266  BOOST_TEST
267  (std::addressof(fitHitInfo), std::addressof(expectedFitHitInfo));
268  BOOST_TEST(fitHitInfo.size() == expectedFitHitInfo.size());
269 
270  BOOST_TEST
271  (trackProxy.get<tag::SpecialHits>().size(), expectedHits.size());
272 
273  // trajectory?
274  BOOST_TEST
275  (trackProxy.hasOriginalTrajectory() == !expectedTrajPtr.isNull());
276  if (expectedTrajCPtr) {
277  BOOST_TEST(trackProxy.originalTrajectoryPtr() == expectedTrajPtr);
278  BOOST_TEST(&trackProxy.originalTrajectory() == expectedTrajPtr.get());
279  }
280  else {
281  BOOST_TEST(!(trackProxy.originalTrajectoryPtr()));
282  }
283 
284  BOOST_TEST(
285  trackProxy(proxy::Tracks::Fitted) ==
286  std::addressof(expectedTrack.Trajectory())
287  );
288  BOOST_TEST(trackProxy(proxy::Tracks::Unfitted) == expectedTrajCPtr);
289  BOOST_TEST(trackProxy(proxy::Tracks::NTypes) == nullptr);
290 
291  // direct interface to recob::Track
292  BOOST_TEST(trackProxy->NPoints() == expectedTrack.NPoints());
293 
294 
295  std::array<unsigned int, recob::TrajectoryPointFlagTraits::maxFlags()>
296  flagCounts;
297  flagCounts.fill(0U);
298  std::size_t iPoint = 0;
299  for (auto const& pointInfo: trackProxy.points()) {
300  BOOST_TEST_CHECKPOINT(" point #" << pointInfo.index());
301 
302  decltype(auto) expectedPointFlags = expectedTrack.FlagsAtPoint(iPoint);
303 
304  BOOST_TEST(pointInfo.index() == iPoint);
305  BOOST_TEST(
306  pointInfo.position() ==
307  expectedTrack.Trajectory().LocationAtPoint(iPoint)
308  );
309  BOOST_TEST
310  (pointInfo.momentum() == expectedTrack.MomentumVectorAtPoint(iPoint));
311  BOOST_TEST(pointInfo.flags() == expectedPointFlags);
312  if (expectedPointFlags.hasOriginalHitIndex()) {
313  BOOST_TEST
314  (pointInfo.hitPtr().key() == expectedPointFlags.fromHit());
315  }
316  else {
317  BOOST_TEST(!pointInfo.hitPtr());
318  }
319 
320  // collect the count of each flag type
321  for (auto flag: {
326  })
327  {
328  if (!expectedPointFlags.isDefined(flag)) continue;
329  if (expectedPointFlags.isSet(flag)) ++flagCounts[flag.index()];
330  }
331 
332  BOOST_TEST
333  (fitHitInfo[iPoint].WireId() == expectedFitHitInfo[iPoint].WireId());
334  BOOST_TEST
335  (pointInfo.fitInfoPtr() == std::addressof(expectedFitHitInfo[iPoint]));
336  BOOST_TEST(
337  std::addressof(fitHitInfo[iPoint]) ==
338  std::addressof(expectedFitHitInfo[iPoint])
339  );
340 
341  ++iPoint;
342  } // for
343  BOOST_TEST(iPoint == expectedTrack.NPoints());
344 
345  // testing pointsWithFlags() with some single flags
346  for (auto flag: {
351  })
352  {
353  BOOST_TEST_CHECKPOINT(" flag: " << flag);
354  unsigned int flagCount = 0U;
355  for (auto const& pointInfo: trackProxy.pointsWithFlags(flag)) {
356 
357  BOOST_TEST_CHECKPOINT(" point #" << pointInfo.index());
358  BOOST_TEST(pointInfo.flags().isDefined(flag));
359  BOOST_TEST(pointInfo.flags().isSet(flag));
360 
361  ++flagCount;
362  } // for pointInfo
363  BOOST_TEST(flagCount == flagCounts[flag.index()]);
364  } // for flag
365 
366  ++iExpectedTrack;
367  } // for
368  BOOST_TEST(iExpectedTrack == expectedTracks.size());
369 
370 } // TrackProxyTest::testTracks()
371 
372 
373 //------------------------------------------------------------------------------
374 void TrackProxyTest::analyze(art::Event const& event) {
375 
376  // "test" that track proxies survive their collection (part I)
377  const double minLength = 30.0;
378  auto const longTracks = getLongTracks(event, minLength);
379 
380  // usage example (supposed to be educational)
381  proxyUsageExample(event);
382 
383  // actual test
384  testTracks(event);
385 
386  // "test" that track proxies survive their collection (part II)
387  mf::LogVerbatim("TrackProxyTest")
388  << longTracks.size() << " tracks are longer than " << minLength << " cm:";
389  std::for_each(longTracks.begin(), longTracks.end(),
390  [this](auto const& track){ this->processTrack(track); });
391 
392 } // TrackProxyTest::analyze()
393 
394 
395 //------------------------------------------------------------------------------
396 
397 DEFINE_ART_MODULE(TrackProxyTest)
auto withOriginalTrajectory(art::InputTag const &inputTag)
Adds recob::TrackTrajectory information to the proxy.
static constexpr Flag_t Suspicious
The point reconstruction is somehow questionable.
ClusterModuleLabel join with tracks
void testTracks(art::Event const &event)
Performs the actual test.
TrackProxyTest & operator=(TrackProxyTest const &)=delete
static constexpr Flag_t NoPoint
The trajectory point is not defined.
art::EDAnalyzer::Table< Config > Parameters
then echo unknown compiler flag
fhicl::Atom< art::InputTag > tracksTag
Declaration of signal hit object.
float Integral() const
Integral under the calibrated signal waveform of the hit, in tick x ADC units.
Definition: Hit.h:224
process_name use argoneut_mc_hitfinder track
process_name hit
Definition: cheaterreco.fcl:51
Runs a test of proxy::Tracks interface.
Definition: Data.h:7
Offers proxy::Tracks and proxy::Track class for recob::Track access.
void proxyUsageExample(art::Event const &event) const
An example of how to access the information via track proxy.
auto getLongTracks(art::Event const &event, double minLength) const
Returns proxies to tracks longer than a certain length.
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
A trajectory in space reconstructed from hits.
Object storing per-hit information from a track fit.
BEGIN_PROLOG vertical distance to the surface Name
Data product for reconstructed trajectory in space.
static constexpr Flag_t HitIgnored
Hit was not included for the computation of the trajectory.
virtual void analyze(art::Event const &event) override
Provides recob::Track data product.
TrackProxyTest(Parameters const &config)
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:218
Represents a track trajectory before the final fit.
art::InputTag tracksTag
Tag for the input tracks.
static constexpr Flag_t DetectorIssue
The hit is associated to a problematic channel.
auto withFitHitInfo(art::InputTag const &inputTag)
Adds recob::TrackFitHitInfo information to the proxy.
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:48
Represents a track trajectory from the final fit.
Number of supported track types.
raw::ChannelID_t Channel() const
ID of the readout channel the hit was extracted from.
Definition: Hit.h:230
void processTrack(Track const &track) const
Single-track processing function example.
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 processPoint(TrackPoint const &point) const
Single-track-point processing function example.