Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
IterativeVertexFinderTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file IterativeVertexFinderTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include <boost/test/data/test_case.hpp>
10 #include <boost/test/tools/output_test_stream.hpp>
11 #include <boost/test/unit_test.hpp>
12 
40 
41 #include <algorithm>
42 #include <array>
43 #include <cmath>
44 #include <functional>
45 #include <iostream>
46 #include <iterator>
47 #include <memory>
48 #include <optional>
49 #include <random>
50 #include <string>
51 #include <system_error>
52 #include <tuple>
53 #include <type_traits>
54 #include <utility>
55 #include <vector>
56 
57 #include "VertexingDataHelper.hpp"
58 
59 namespace bdata = boost::unit_test::data;
60 using namespace Acts::UnitLiterals;
61 
62 namespace Acts {
63 namespace Test {
64 
67 using Linearizer = HelicalTrackLinearizer<Propagator>;
68 
69 // Create a test context
72 
73 const std::string toolString = "IVF";
74 
75 // Vertex x/y position distribution
76 std::uniform_real_distribution<> vXYDist(-0.1_mm, 0.1_mm);
77 // Vertex z position distribution
78 std::uniform_real_distribution<> vZDist(-20_mm, 20_mm);
79 // Track d0 distribution
80 std::uniform_real_distribution<> d0Dist(-0.01_mm, 0.01_mm);
81 // Track z0 distribution
82 std::uniform_real_distribution<> z0Dist(-0.2_mm, 0.2_mm);
83 // Track pT distribution
84 std::uniform_real_distribution<> pTDist(0.4_GeV, 10_GeV);
85 // Track phi distribution
86 std::uniform_real_distribution<> phiDist(-M_PI, M_PI);
87 // Track theta distribution
88 std::uniform_real_distribution<> thetaDist(1.0, M_PI - 1.0);
89 // Track charge helper distribution
90 std::uniform_real_distribution<> qDist(-1, 1);
91 // Track IP resolution distribution
92 std::uniform_real_distribution<> resIPDist(0., 100_um);
93 // Track angular distribution
94 std::uniform_real_distribution<> resAngDist(0., 0.1);
95 // Track q/p resolution distribution
96 std::uniform_real_distribution<> resQoPDist(-0.01, 0.01);
97 // Number of vertices per test event distribution
98 std::uniform_int_distribution<> nVertexDist(1, 6);
99 // Number of tracks per vertex distribution
100 std::uniform_int_distribution<> nTracksDist(5, 15);
101 
102 // Dummy user-defined InputTrack type
103 struct InputTrack {
104  InputTrack(const BoundTrackParameters& params) : m_parameters(params) {}
105 
106  const BoundTrackParameters& parameters() const { return m_parameters; }
107 
108  // store e.g. link to original objects here
109 
110  private:
111  BoundTrackParameters m_parameters;
112 };
113 
117 BOOST_AUTO_TEST_CASE(iterative_finder_test) {
118  bool debug = false;
119 
120  // Set up RNG
121  int mySeed = 31415;
122  std::mt19937 gen(mySeed);
123 
124  // Number of test events
125  unsigned int nEvents = 5; // = nTest
126 
127  for (unsigned int iEvent = 0; iEvent < nEvents; ++iEvent) {
128  // Set up constant B-Field
129  auto bField = std::make_shared<ConstantBField>(Vector3{0.0, 0.0, 1_T});
130 
131  // Set up Eigenstepper
133 
134  // Set up propagator with void navigator
135  auto propagator = std::make_shared<Propagator>(stepper);
136 
137  // Linearizer for BoundTrackParameters type test
138  Linearizer::Config ltConfig(bField, propagator);
139  Linearizer linearizer(ltConfig);
140 
141  using BilloirFitter =
143 
144  // Set up Billoir Vertex Fitter
145  BilloirFitter::Config vertexFitterCfg;
146 
147  BilloirFitter bFitter(vertexFitterCfg);
148 
149  // Impact point estimator
151 
152  IPEstimator::Config ipEstimatorCfg(bField, propagator);
153  IPEstimator ipEstimator(ipEstimatorCfg);
154 
155  using ZScanSeedFinder = ZScanVertexFinder<BilloirFitter>;
156 
157  static_assert(VertexFinderConcept<ZScanSeedFinder>,
158  "Vertex finder does not fulfill vertex finder concept.");
159 
160  ZScanSeedFinder::Config seedFinderCfg(ipEstimator);
161 
162  ZScanSeedFinder sFinder(seedFinderCfg);
163 
164  // Vertex Finder
166 
167  static_assert(VertexFinderConcept<VertexFinder>,
168  "Vertex finder does not fulfill vertex finder concept.");
169 
170  VertexFinder::Config cfg(bFitter, std::move(linearizer), std::move(sFinder),
171  ipEstimator);
172 
173  cfg.reassignTracksAfterFirstFit = true;
174 
175  VertexFinder finder(cfg);
176  VertexFinder::State state(*bField, magFieldContext);
177 
178  // Vector to be filled with all tracks in current event
179  std::vector<std::unique_ptr<const BoundTrackParameters>> tracks;
180 
181  std::vector<const BoundTrackParameters*> tracksPtr;
182 
183  // Vector to be filled with truth vertices for later comparison
184  std::vector<Vertex<BoundTrackParameters>> trueVertices;
185 
186  // start creating event with nVertices vertices
187  unsigned int nVertices = nVertexDist(gen);
188  for (unsigned int iVertex = 0; iVertex < nVertices; ++iVertex) {
189  // Number of tracks
190  unsigned int nTracks = nTracksDist(gen);
191 
192  if (debug) {
193  std::cout << "Event " << iEvent << ", Vertex " << iVertex << "/"
194  << nVertices << " with " << nTracks << " tracks."
195  << std::endl;
196  }
197  // Create perigee surface
198  std::shared_ptr<PerigeeSurface> perigeeSurface =
199  Surface::makeShared<PerigeeSurface>(Vector3(0., 0., 0.));
200 
201  // Create position of vertex and perigee surface
202  double x = vXYDist(gen);
203  double y = vXYDist(gen);
204  double z = vZDist(gen);
205 
206  // True vertex
207  Vertex<BoundTrackParameters> trueV(Vector3(x, y, z));
208  std::vector<TrackAtVertex<BoundTrackParameters>> tracksAtTrueVtx;
209 
210  // Calculate d0 and z0 corresponding to vertex position
211  double d0_v = std::hypot(x, y);
212  double z0_v = z;
213 
214  // Construct random track emerging from vicinity of vertex position
215  // Vector to store track objects used for vertex fit
216  for (unsigned int iTrack = 0; iTrack < nTracks; iTrack++) {
217  // Construct positive or negative charge randomly
218  double q = qDist(gen) < 0 ? -1. : 1.;
219 
220  // Construct random track parameters
221  BoundVector paramVec;
222  double z0track = z0_v + z0Dist(gen);
223  paramVec << d0_v + d0Dist(gen), z0track, phiDist(gen), thetaDist(gen),
224  q / pTDist(gen), 0.;
225 
226  // Resolutions
227  double res_d0 = resIPDist(gen);
228  double res_z0 = resIPDist(gen);
229  double res_ph = resAngDist(gen);
230  double res_th = resAngDist(gen);
231  double res_qp = resQoPDist(gen);
232 
233  // Fill vector of track objects with simple covariance matrix
234  Covariance covMat;
235  covMat << res_d0 * res_d0, 0., 0., 0., 0., 0., 0., res_z0 * res_z0, 0.,
236  0., 0., 0., 0., 0., res_ph * res_ph, 0., 0., 0., 0., 0., 0.,
237  res_th * res_th, 0., 0., 0., 0., 0., 0., res_qp * res_qp, 0., 0.,
238  0., 0., 0., 0., 1.;
239  auto params =
240  BoundTrackParameters(perigeeSurface, paramVec, std::move(covMat),
242 
243  tracks.push_back(std::make_unique<BoundTrackParameters>(params));
244 
245  TrackAtVertex<BoundTrackParameters> trAtVt(0., params,
246  tracks.back().get());
247  tracksAtTrueVtx.push_back(trAtVt);
248  }
249 
250  trueV.setTracksAtVertex(tracksAtTrueVtx);
251  trueVertices.push_back(trueV);
252 
253  } // end loop over vertices
254 
255  // shuffle list of tracks
256  std::shuffle(std::begin(tracks), std::end(tracks), gen);
257 
258  for (const auto& trk : tracks) {
259  tracksPtr.push_back(trk.get());
260  }
261 
264 
265  // find vertices
266  auto res = finder.find(tracksPtr, vertexingOptions, state);
267 
268  BOOST_CHECK(res.ok());
269 
270  if (!res.ok()) {
271  std::cout << res.error().message() << std::endl;
272  }
273 
274  // Retrieve vertices found by vertex finder
275  auto vertexCollection = *res;
276 
277  // check if same amount of vertices has been found with tolerance of 2
278  CHECK_CLOSE_ABS(vertexCollection.size(), nVertices, 2);
279 
280  if (debug) {
281  std::cout << "########## RESULT: ########## Event " << iEvent
282  << std::endl;
283  std::cout << "Number of true vertices: " << nVertices << std::endl;
284  std::cout << "Number of reco vertices: " << vertexCollection.size()
285  << std::endl;
286 
287  int count = 1;
288  std::cout << "----- True vertices -----" << std::endl;
289  for (const auto& vertex : trueVertices) {
290  Vector3 pos = vertex.position();
291  std::cout << count << ". True Vertex:\t Position:"
292  << "(" << pos[eX] << "," << pos[eY] << "," << pos[eZ] << ")"
293  << std::endl;
294  std::cout << "Number of tracks: " << vertex.tracks().size() << std::endl
295  << std::endl;
296  count++;
297  }
298  std::cout << "----- Reco vertices -----" << std::endl;
299  count = 1;
300  for (const auto& vertex : vertexCollection) {
301  Vector3 pos = vertex.position();
302  std::cout << count << ". Reco Vertex:\t Position:"
303  << "(" << pos[eX] << "," << pos[eY] << "," << pos[eZ] << ")"
304  << std::endl;
305  std::cout << "Number of tracks: " << vertex.tracks().size() << std::endl
306  << std::endl;
307  count++;
308  }
309  }
310 
311  // Check if all vertices have been found with close z-values
312  bool allVerticesFound = true;
313  for (const auto& trueVertex : trueVertices) {
314  Vector4 truePos = trueVertex.fullPosition();
315  bool currentVertexFound = false;
316  for (const auto& recoVertex : vertexCollection) {
317  Vector4 recoPos = recoVertex.fullPosition();
318  // check only for close z distance
319  double zDistance = std::abs(truePos[eZ] - recoPos[eZ]);
320  if (zDistance < 2_mm) {
321  currentVertexFound = true;
322  }
323  }
324  if (!currentVertexFound) {
325  allVerticesFound = false;
326  }
327  }
328 
329  // check if found vertices have compatible z values
330  BOOST_CHECK(allVerticesFound);
331  }
332 }
333 
338 BOOST_AUTO_TEST_CASE(iterative_finder_test_user_track_type) {
339  bool debug = false;
340 
341  // Set up RNG
342  int mySeed = 31415;
343  std::mt19937 gen(mySeed);
344 
345  // Number of test events
346  unsigned int nEvents = 5; // = nTest
347 
348  for (unsigned int iEvent = 0; iEvent < nEvents; ++iEvent) {
349  // Set up constant B-Field
350  auto bField = std::make_shared<ConstantBField>(Vector3{0.0, 0.0, 1_T});
351 
352  // Set up Eigenstepper
354 
355  // Set up propagator with void navigator
356  auto propagator = std::make_shared<Propagator>(stepper);
357 
358  // Linearizer for user defined InputTrack type test
359  Linearizer::Config ltConfigUT(bField, propagator);
360  Linearizer linearizer(ltConfigUT);
361 
362  // Set up vertex fitter for user track type
364 
365  // Create a custom std::function to extract BoundTrackParameters from
366  // user-defined InputTrack
367  std::function<BoundTrackParameters(InputTrack)> extractParameters =
368  [](const InputTrack& params) { return params.parameters(); };
369 
370  // Set up Billoir Vertex Fitter
371  BilloirFitter::Config vertexFitterCfg;
372 
373  BilloirFitter bFitter(vertexFitterCfg, extractParameters);
374 
375  // IP Estimator
377 
378  IPEstimator::Config ipEstimatorCfg(bField, propagator);
379  IPEstimator ipEstimator(ipEstimatorCfg);
380 
381  using ZScanSeedFinder = ZScanVertexFinder<BilloirFitter>;
382  ZScanSeedFinder::Config seedFinderCfg(ipEstimator);
383 
384  ZScanSeedFinder sFinder(seedFinderCfg, extractParameters);
385 
386  // Vertex Finder
388  VertexFinder::Config cfg(bFitter, std::move(linearizer), std::move(sFinder),
389  ipEstimator);
390  cfg.reassignTracksAfterFirstFit = true;
391 
392  VertexFinder finder(cfg, extractParameters);
393  VertexFinder::State state(*bField, magFieldContext);
394 
395  // Same for user track type tracks
396  std::vector<std::unique_ptr<const InputTrack>> tracks;
397 
398  std::vector<const InputTrack*> tracksPtr;
399 
400  // Vector to be filled with truth vertices for later comparison
401  std::vector<Vertex<InputTrack>> trueVertices;
402 
403  // start creating event with nVertices vertices
404  unsigned int nVertices = nVertexDist(gen);
405  for (unsigned int iVertex = 0; iVertex < nVertices; ++iVertex) {
406  // Number of tracks
407  unsigned int nTracks = nTracksDist(gen);
408 
409  if (debug) {
410  std::cout << "Event " << iEvent << ", Vertex " << iVertex << "/"
411  << nVertices << " with " << nTracks << " tracks."
412  << std::endl;
413  }
414  // Create perigee surface
415  std::shared_ptr<PerigeeSurface> perigeeSurface =
416  Surface::makeShared<PerigeeSurface>(Vector3(0., 0., 0.));
417 
418  // Create position of vertex and perigee surface
419  double x = vXYDist(gen);
420  double y = vXYDist(gen);
421  double z = vZDist(gen);
422 
423  // True vertex
424  Vertex<InputTrack> trueV(Vector3(x, y, z));
425  std::vector<TrackAtVertex<InputTrack>> tracksAtTrueVtx;
426 
427  // Calculate d0 and z0 corresponding to vertex position
428  double d0_v = std::hypot(x, y);
429  double z0_v = z;
430 
431  // Construct random track emerging from vicinity of vertex position
432  // Vector to store track objects used for vertex fit
433  for (unsigned int iTrack = 0; iTrack < nTracks; iTrack++) {
434  // Construct positive or negative charge randomly
435  double q = qDist(gen) < 0 ? -1. : 1.;
436 
437  // Construct random track parameters
438  BoundVector paramVec;
439  double z0track = z0_v + z0Dist(gen);
440  paramVec << d0_v + d0Dist(gen), z0track, phiDist(gen), thetaDist(gen),
441  q / pTDist(gen), 0.;
442 
443  // Resolutions
444  double res_d0 = resIPDist(gen);
445  double res_z0 = resIPDist(gen);
446  double res_ph = resAngDist(gen);
447  double res_th = resAngDist(gen);
448  double res_qp = resQoPDist(gen);
449 
450  // Fill vector of track objects now for user track type
451  Covariance covMat;
452 
453  covMat << res_d0 * res_d0, 0., 0., 0., 0., 0., 0., res_z0 * res_z0, 0.,
454  0., 0., 0., 0., 0., res_ph * res_ph, 0., 0., 0., 0., 0., 0.,
455  res_th * res_th, 0., 0., 0., 0., 0., 0., res_qp * res_qp, 0., 0.,
456  0., 0., 0., 0., 1.;
457  auto paramsUT = InputTrack(
458  BoundTrackParameters(perigeeSurface, paramVec, std::move(covMat),
460 
461  tracks.push_back(std::make_unique<InputTrack>(paramsUT));
462 
463  auto params = extractParameters(paramsUT);
464 
465  TrackAtVertex<InputTrack> trAtVt(0., params, tracks.back().get());
466  tracksAtTrueVtx.push_back(trAtVt);
467  }
468 
469  trueV.setTracksAtVertex(tracksAtTrueVtx);
470  trueVertices.push_back(trueV);
471 
472  } // end loop over vertices
473 
474  // shuffle list of tracks
475  std::shuffle(std::begin(tracks), std::end(tracks), gen);
476 
477  for (const auto& trk : tracks) {
478  tracksPtr.push_back(trk.get());
479  }
480 
481  VertexingOptions<InputTrack> vertexingOptionsUT(geoContext,
483 
484  // find vertices
485  auto res = finder.find(tracksPtr, vertexingOptionsUT, state);
486 
487  BOOST_CHECK(res.ok());
488 
489  if (!res.ok()) {
490  std::cout << res.error().message() << std::endl;
491  }
492 
493  // Retrieve vertices found by vertex finder
494  auto vertexCollectionUT = *res;
495 
496  // check if same amount of vertices has been found with tolerance of 2
497  CHECK_CLOSE_ABS(vertexCollectionUT.size(), nVertices, 2);
498 
499  if (debug) {
500  std::cout << "########## RESULT: ########## Event " << iEvent
501  << std::endl;
502  std::cout << "Number of true vertices: " << nVertices << std::endl;
503  std::cout << "Number of reco vertices: " << vertexCollectionUT.size()
504  << std::endl;
505 
506  int count = 1;
507  std::cout << "----- True vertices -----" << std::endl;
508  for (const auto& vertex : trueVertices) {
509  Vector3 pos = vertex.position();
510  std::cout << count << ". True Vertex:\t Position:"
511  << "(" << pos[eX] << "," << pos[eY] << "," << pos[eZ] << ")"
512  << std::endl;
513  std::cout << "Number of tracks: " << vertex.tracks().size() << std::endl
514  << std::endl;
515  count++;
516  }
517  std::cout << "----- Reco vertices -----" << std::endl;
518  count = 1;
519  for (const auto& vertex : vertexCollectionUT) {
520  Vector3 pos = vertex.position();
521  std::cout << count << ". Reco Vertex:\t Position:"
522  << "(" << pos[eX] << "," << pos[eY] << "," << pos[eZ] << ")"
523  << std::endl;
524  std::cout << "Number of tracks: " << vertex.tracks().size() << std::endl
525  << std::endl;
526  count++;
527  }
528  }
529 
530  // Check if all vertices have been found with close z-values
531  bool allVerticesFound = true;
532  for (const auto& trueVertex : trueVertices) {
533  Vector4 truePos = trueVertex.fullPosition();
534  bool currentVertexFound = false;
535  for (const auto& recoVertex : vertexCollectionUT) {
536  Vector4 recoPos = recoVertex.fullPosition();
537  // check only for close z distance
538  double zDistance = std::abs(truePos[eZ] - recoPos[eZ]);
539  if (zDistance < 2_mm) {
540  currentVertexFound = true;
541  }
542  }
543  if (!currentVertexFound) {
544  allVerticesFound = false;
545  }
546  }
547 
548  // check if found vertices have compatible z values
549  BOOST_CHECK(allVerticesFound);
550  }
551 }
552 
556 BOOST_AUTO_TEST_CASE(iterative_finder_test_athena_reference) {
557  // Set up constant B-Field
558  auto bField = std::make_shared<ConstantBField>(Vector3{0.0, 0.0, 2_T});
559 
560  // Set up Eigenstepper
562 
563  // Set up propagator with void navigator
564  auto propagator = std::make_shared<Propagator>(stepper);
565 
566  // Linearizer for BoundTrackParameters type test
567  Linearizer::Config ltConfig(bField, propagator);
568  Linearizer linearizer(ltConfig);
569 
570  using BilloirFitter =
572 
573  // Set up Billoir Vertex Fitter
574  BilloirFitter::Config vertexFitterCfg;
575 
576  BilloirFitter bFitter(vertexFitterCfg);
577 
578  // Impact point estimator
580 
581  IPEstimator::Config ipEstimatorCfg(bField, propagator);
582  IPEstimator ipEstimator(ipEstimatorCfg);
583 
584  using ZScanSeedFinder = ZScanVertexFinder<BilloirFitter>;
585 
586  static_assert(VertexFinderConcept<ZScanSeedFinder>,
587  "Vertex finder does not fulfill vertex finder concept.");
588 
589  ZScanSeedFinder::Config seedFinderCfg(ipEstimator);
590 
591  ZScanSeedFinder sFinder(seedFinderCfg);
592 
593  // Vertex Finder
595 
596  static_assert(VertexFinderConcept<VertexFinder>,
597  "Vertex finder does not fulfill vertex finder concept.");
598 
599  VertexFinder::Config cfg(bFitter, std::move(linearizer), std::move(sFinder),
600  ipEstimator);
601  cfg.maxVertices = 200;
602  cfg.maximumChi2cutForSeeding = 49;
603  cfg.significanceCutSeeding = 12;
604 
605  VertexFinder finder(cfg);
606  VertexFinder::State state(*bField, magFieldContext);
607 
608  auto csvData = readTracksAndVertexCSV(toolString);
609  auto tracks = std::get<TracksData>(csvData);
610 
611  std::vector<const BoundTrackParameters*> tracksPtr;
612  for (const auto& trk : tracks) {
613  tracksPtr.push_back(&trk);
614  }
615 
616  Vertex<BoundTrackParameters> beamSpot = std::get<BeamSpotData>(csvData);
618  geoContext, magFieldContext, beamSpot);
619 
620  // find vertices
621  auto findResult = finder.find(tracksPtr, vertexingOptions, state);
622 
623  // BOOST_CHECK(findResult.ok());
624 
625  if (!findResult.ok()) {
626  std::cout << findResult.error().message() << std::endl;
627  }
628 
629  // Retrieve vertices found by vertex finder
630  // std::vector<Vertex<BoundTrackParameters>> allVertices = *findResult;
631 
632  // Test expected outcomes from athena implementation
633  // Number of reconstructed vertices
634  // auto verticesInfo = std::get<VerticesData>(csvData);
635  // const int expNRecoVertices = verticesInfo.size();
636 
637  // BOOST_CHECK_EQUAL(allVertices.size(), expNRecoVertices);
638  // for (int i = 0; i < expNRecoVertices; i++) {
639  // auto recoVtx = allVertices[i];
640  // auto expVtx = verticesInfo[i];
641  // CHECK_CLOSE_ABS(recoVtx.position(), expVtx.position, 0.001_mm);
642  // CHECK_CLOSE_ABS(recoVtx.covariance(), expVtx.covariance, 0.001_mm);
643  // BOOST_CHECK_EQUAL(recoVtx.tracks().size(), expVtx.nTracks);
644  // CHECK_CLOSE_ABS(recoVtx.tracks()[0].trackWeight, expVtx.trk1Weight,
645  // 0.003); CHECK_CLOSE_ABS(recoVtx.tracks()[0].vertexCompatibility,
646  // expVtx.trk1Comp,
647  // 0.003);
648  // }
649 }
650 
651 } // namespace Test
652 } // namespace Acts