ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
IterativeVertexFinder.ipp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file IterativeVertexFinder.ipp
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 template <typename vfitter_t, typename sfinder_t>
11  const std::vector<const InputTrack_t*>& trackVector,
12  const VertexingOptions<InputTrack_t>& vertexingOptions) const
14  // Original tracks
15  const std::vector<const InputTrack_t*>& origTracks = trackVector;
16  // Tracks for seeding
17  std::vector<const InputTrack_t*> seedTracks = trackVector;
18 
19  // List of vertices to be filled below
20  std::vector<Vertex<InputTrack_t>> vertexCollection;
21 
22  int nInterations = 0;
23  // begin iterating
24  while (seedTracks.size() > 1 && nInterations < m_cfg.maxVertices) {
26  auto seedRes = getVertexSeed(seedTracks, vertexingOptions);
27  if (!seedRes.ok()) {
28  return seedRes.error();
29  }
30  // retrieve the seed vertex as the last element in
31  // the seed vertexCollection
32  Vertex<InputTrack_t>& seedVertex = *seedRes;
35  // Tracks used for the fit in this iteration
36  std::vector<const InputTrack_t*> perigeesToFit;
37  std::vector<const InputTrack_t*> perigeesToFitSplitVertex;
38 
39  // Fill vector with tracks to fit, only compatible with seed:
40  auto res = fillPerigeesToFit(seedTracks, seedVertex, perigeesToFit,
41  perigeesToFitSplitVertex, vertexingOptions);
42 
43  if (!res.ok()) {
44  return res.error();
45  }
46 
47  ACTS_DEBUG("Perigees used for fit: " << perigeesToFit.size());
48 
50  Vertex<InputTrack_t> currentVertex;
51  Vertex<InputTrack_t> currentSplitVertex;
52 
53  if (m_cfg.useBeamConstraint && !perigeesToFit.empty()) {
54  auto fitResult = m_cfg.vertexFitter.fit(perigeesToFit, m_cfg.linearizer,
55  vertexingOptions);
56  if (fitResult.ok()) {
57  currentVertex = std::move(*fitResult);
58  } else {
59  return fitResult.error();
60  }
61  } else if (!m_cfg.useBeamConstraint && perigeesToFit.size() > 1) {
62  auto fitResult = m_cfg.vertexFitter.fit(perigeesToFit, m_cfg.linearizer,
63  vertexingOptions);
64  if (fitResult.ok()) {
65  currentVertex = std::move(*fitResult);
66  } else {
67  return fitResult.error();
68  }
69  }
70  if (m_cfg.createSplitVertices && perigeesToFitSplitVertex.size() > 1) {
71  auto fitResult = m_cfg.vertexFitter.fit(
72  perigeesToFitSplitVertex, m_cfg.linearizer, vertexingOptions);
73  if (fitResult.ok()) {
74  currentSplitVertex = std::move(*fitResult);
75  } else {
76  return fitResult.error();
77  }
78  }
80  ACTS_DEBUG("Vertex position after fit: " << currentVertex.fullPosition());
81 
82  // Number degrees of freedom
83  double ndf = currentVertex.fitQuality().second;
84  double ndfSplitVertex = currentSplitVertex.fitQuality().second;
85 
86  // Number of significant tracks
87  int nTracksAtVertex = countSignificantTracks(currentVertex);
88  int nTracksAtSplitVertex = countSignificantTracks(currentSplitVertex);
89 
90  bool isGoodVertex =
91  ((!m_cfg.useBeamConstraint && ndf > 0 && nTracksAtVertex >= 2) ||
92  (m_cfg.useBeamConstraint && ndf > 3 && nTracksAtVertex >= 2));
93 
94  if (!isGoodVertex) {
95  removeAllTracks(perigeesToFit, seedTracks);
96  } else {
97  if (m_cfg.reassignTracksAfterFirstFit && (!m_cfg.createSplitVertices)) {
98  // vertex is good vertex here
99  // but add tracks which may have been missed
100 
101  auto result = reassignTracksToNewVertex(vertexCollection, currentVertex,
102  perigeesToFit, seedTracks,
103  origTracks, vertexingOptions);
104  if (!result.ok()) {
105  return result.error();
106  }
107  isGoodVertex = *result;
108 
109  } // end reassignTracksAfterFirstFit case
110  // still good vertex? might have changed in the meanwhile
111  if (isGoodVertex) {
112  removeUsedCompatibleTracks(currentVertex, perigeesToFit, seedTracks,
113  vertexingOptions);
114 
115  ACTS_DEBUG(
116  "Number of seed tracks after removal of compatible tracks "
117  "and outliers: "
118  << seedTracks.size());
119  }
120  } // end case if good vertex
121 
122  // now splitvertex
123  bool isGoodSplitVertex = false;
124  if (m_cfg.createSplitVertices) {
125  isGoodSplitVertex = (ndfSplitVertex > 0 && nTracksAtSplitVertex >= 2);
126 
127  if (!isGoodSplitVertex) {
128  removeAllTracks(perigeesToFitSplitVertex, seedTracks);
129  } else {
130  removeUsedCompatibleTracks(currentSplitVertex, perigeesToFitSplitVertex,
131  seedTracks, vertexingOptions);
132  }
133  }
134  // Now fill vertex collection with vertex
135  if (isGoodVertex) {
136  vertexCollection.push_back(currentVertex);
137  }
138  if (isGoodSplitVertex && m_cfg.createSplitVertices) {
139  vertexCollection.push_back(currentSplitVertex);
140  }
141 
142  nInterations++;
143 
144  } // end while loop
145 
146  return vertexCollection;
147 }
148 
149 template <typename vfitter_t, typename sfinder_t>
151  const std::vector<const InputTrack_t*>& seedTracks,
152  const VertexingOptions<InputTrack_t>& vertexingOptions) const
154  auto res = m_cfg.seedFinder.find(seedTracks, vertexingOptions);
155  if (res.ok()) {
156  auto vertexCollection = *res;
157  if (vertexCollection.empty()) {
158  ACTS_DEBUG(
159  "No seed found. Number of input tracks: " << seedTracks.size());
160  return VertexingError::SeedingError;
161  }
162  // current seed is last element in collection
163  Vertex<InputTrack_t> seedVertex = vertexCollection.back();
164  ACTS_DEBUG("Seed found at position: ("
165  << seedVertex.fullPosition()[eX] << ", "
166  << seedVertex.fullPosition()[eY] << ", "
167  << seedVertex.fullPosition()[eZ] << ", " << seedVertex.time()
168  << "). Number of input tracks: " << seedTracks.size());
169  return seedVertex;
170  } else {
171  ACTS_DEBUG("No seed found. Number of input tracks: " << seedTracks.size());
172  return VertexingError::SeedingError;
173  }
174 }
175 
176 template <typename vfitter_t, typename sfinder_t>
178  const std::vector<const InputTrack_t*>& perigeesToFit,
179  std::vector<const InputTrack_t*>& seedTracks) const {
180  for (const auto& fitPerigee : perigeesToFit) {
181  const BoundParameters& fitPerigeeParams = m_extractParameters(*fitPerigee);
182  // Find track in seedTracks
183  auto foundIter =
184  std::find_if(seedTracks.begin(), seedTracks.end(),
185  [&fitPerigeeParams, this](const auto seedTrk) {
186  return fitPerigeeParams == m_extractParameters(*seedTrk);
187  });
188  if (foundIter != seedTracks.end()) {
189  // Remove track from seed tracks
190  seedTracks.erase(foundIter);
191  } else {
192  ACTS_WARNING("Track to be removed not found in seed tracks.")
193  }
194  }
195 }
196 
197 template <typename vfitter_t, typename sfinder_t>
200  const BoundParameters& params, const Vertex<InputTrack_t>& vertex,
201  const VertexingOptions<InputTrack_t>& vertexingOptions) const {
202  // Linearize track
203  auto result = m_cfg.linearizer.linearizeTrack(
204  params, vertex.fullPosition(), vertexingOptions.geoContext,
205  vertexingOptions.magFieldContext);
206  if (!result.ok()) {
207  return result.error();
208  }
209 
210  auto linTrack = std::move(*result);
211 
212  // Calculate reduced weight
213  ActsSymMatrixD<2> weightReduced =
214  linTrack.covarianceAtPCA.template block<2, 2>(0, 0);
215 
216  ActsSymMatrixD<2> errorVertexReduced =
217  (linTrack.positionJacobian *
218  (vertex.fullCovariance() * linTrack.positionJacobian.transpose()))
219  .template block<2, 2>(0, 0);
220  weightReduced += errorVertexReduced;
221  weightReduced = weightReduced.inverse();
222 
223  // Calculate compatibility / chi2
224  Vector2D trackParameters2D =
225  linTrack.parametersAtPCA.template block<2, 1>(0, 0);
226  double compatibility =
227  trackParameters2D.dot(weightReduced * trackParameters2D);
228 
229  return compatibility;
230 }
231 
232 template <typename vfitter_t, typename sfinder_t>
235  Vertex<InputTrack_t>& myVertex,
236  std::vector<const InputTrack_t*>& perigeesToFit,
237  std::vector<const InputTrack_t*>& seedTracks,
238  const VertexingOptions<InputTrack_t>& vertexingOptions) const {
239  std::vector<TrackAtVertex<InputTrack_t>> tracksAtVertex = myVertex.tracks();
240 
241  for (const auto& trackAtVtx : tracksAtVertex) {
242  // Check compatibility
243  if (trackAtVtx.trackWeight < m_cfg.cutOffTrackWeight) {
244  // Do not remove track here, since it is not compatible with the vertex
245  continue;
246  }
247  // Find and remove track from seedTracks
248  auto foundSeedIter =
249  std::find_if(seedTracks.begin(), seedTracks.end(),
250  [&trackAtVtx, this](const auto seedTrk) {
251  return trackAtVtx.originalParams == seedTrk;
252  });
253  if (foundSeedIter != seedTracks.end()) {
254  seedTracks.erase(foundSeedIter);
255  } else {
256  ACTS_WARNING("Track trackAtVtx not found in seedTracks!");
257  }
258 
259  // Find and remove track from perigeesToFit
260  auto foundFitIter =
261  std::find_if(perigeesToFit.begin(), perigeesToFit.end(),
262  [&trackAtVtx, this](const auto fitTrk) {
263  return trackAtVtx.originalParams == fitTrk;
264  });
265  if (foundFitIter != perigeesToFit.end()) {
266  perigeesToFit.erase(foundFitIter);
267  } else {
268  ACTS_WARNING("Track trackAtVtx not found in perigeesToFit!");
269  }
270  } // end iteration over tracksAtVertex
271 
272  ACTS_DEBUG("After removal of tracks belonging to vertex, "
273  << seedTracks.size() << " seed tracks left.");
274 
275  // Now start considering outliers
276  // perigeesToFit that are left here were below
277  // m_cfg.cutOffTrackWeight threshold and are hence outliers
278  ACTS_DEBUG("Number of outliers: " << perigeesToFit.size());
279 
280  for (const auto& myPerigeeToFit : perigeesToFit) {
281  // calculate chi2 w.r.t. last fitted vertex
282  auto result = getCompatibility(m_extractParameters(*myPerigeeToFit),
283  myVertex, vertexingOptions);
284 
285  if (!result.ok()) {
286  return result.error();
287  }
288 
289  double chi2 = *result;
290 
291  // check if sufficiently compatible with last fitted vertex
292  // (quite loose constraint)
293  if (chi2 < m_cfg.maximumChi2cutForSeeding) {
294  auto foundIter =
295  std::find_if(seedTracks.begin(), seedTracks.end(),
296  [&myPerigeeToFit, this](const auto seedTrk) {
297  return myPerigeeToFit == seedTrk;
298  });
299  if (foundIter != seedTracks.end()) {
300  // Remove track from seed tracks
301  seedTracks.erase(foundIter);
302  }
303 
304  } else {
305  // Track not compatible with vertex
306  // Remove track from current vertex
307  auto foundIter =
308  std::find_if(tracksAtVertex.begin(), tracksAtVertex.end(),
309  [&myPerigeeToFit, this](auto trk) {
310  return myPerigeeToFit == trk.originalParams;
311  });
312  if (foundIter != tracksAtVertex.end()) {
313  // Remove track from seed tracks
314  tracksAtVertex.erase(foundIter);
315  }
316  }
317  }
318 
319  // set updated (possibly with removed outliers) tracksAtVertex to vertex
320  myVertex.setTracksAtVertex(tracksAtVertex);
321 
322  return {};
323 }
324 
325 template <typename vfitter_t, typename sfinder_t>
328  const std::vector<const InputTrack_t*>& perigeeList,
329  const Vertex<InputTrack_t>& seedVertex,
330  std::vector<const InputTrack_t*>& perigeesToFitOut,
331  std::vector<const InputTrack_t*>& perigeesToFitSplitVertexOut,
332  const VertexingOptions<InputTrack_t>& vertexingOptions) const {
333  int numberOfTracks = perigeeList.size();
334 
335  // Count how many tracks are used for fit
336  int count = 0;
337  // Fill perigeesToFit vector with tracks compatible with seed
338  for (const auto& sTrack : perigeeList) {
339  if (numberOfTracks <= 2) {
340  perigeesToFitOut.push_back(sTrack);
341  ++count;
342  } else if (numberOfTracks <= 4 && !m_cfg.createSplitVertices) {
343  perigeesToFitOut.push_back(sTrack);
344  ++count;
345  } else if (numberOfTracks <= 4 * m_cfg.splitVerticesTrkInvFraction &&
346  m_cfg.createSplitVertices) {
347  // Only few tracks left, put them into fit regardless their position
348  if (count % m_cfg.splitVerticesTrkInvFraction == 0) {
349  perigeesToFitOut.push_back(sTrack);
350  ++count;
351  } else {
352  perigeesToFitSplitVertexOut.push_back(sTrack);
353  ++count;
354  }
355  }
356  // still large amount of tracks available, check compatibility
357  else {
358  // check first that distance is not too large
359  const BoundParameters& sTrackParams = m_extractParameters(*sTrack);
360  auto distanceRes = m_cfg.ipEst.calculate3dDistance(
361  vertexingOptions.geoContext, sTrackParams, seedVertex.position());
362  if (!distanceRes.ok()) {
363  return distanceRes.error();
364  }
365 
366  if (!sTrackParams.covariance()) {
367  return VertexingError::NoCovariance;
368  }
369 
370  double error = sqrt((*(sTrackParams.covariance()))(eLOC_D0, eLOC_D0) +
371  (*(sTrackParams.covariance()))(eLOC_Z0, eLOC_Z0));
372 
373  if (error == 0.) {
374  ACTS_WARNING("Error is zero. Setting to 1.");
375  error = 1.;
376  }
377 
378  if (*distanceRes / error < m_cfg.significanceCutSeeding) {
379  if (count % m_cfg.splitVerticesTrkInvFraction == 0 ||
380  !m_cfg.createSplitVertices) {
381  perigeesToFitOut.push_back(sTrack);
382  ++count;
383  } else {
384  perigeesToFitSplitVertexOut.push_back(sTrack);
385  ++count;
386  }
387  }
388  }
389  }
390  return {};
391 }
392 
393 template <typename vfitter_t, typename sfinder_t>
396  std::vector<Vertex<InputTrack_t>>& vertexCollection,
397  Vertex<InputTrack_t>& currentVertex,
398  std::vector<const InputTrack_t*>& perigeesToFit,
399  std::vector<const InputTrack_t*>& seedTracks,
400  const std::vector<const InputTrack_t*>& origTracks,
401  const VertexingOptions<InputTrack_t>& vertexingOptions) const {
402  int numberOfAddedTracks = 0;
403 
404  // iterate over all vertices and check if tracks need to be reassigned
405  // to new (current) vertex
406  for (auto& vertexIt : vertexCollection) {
407  // tracks at vertexIt
408  std::vector<TrackAtVertex<InputTrack_t>> tracksAtVertex = vertexIt.tracks();
409  auto tracksBegin = tracksAtVertex.begin();
410  auto tracksEnd = tracksAtVertex.end();
411 
412  for (auto tracksIter = tracksBegin; tracksIter != tracksEnd;) {
413  // consider only tracks that are not too tightly assigned to other
414  // vertex
415  if (tracksIter->trackWeight > m_cfg.cutOffTrackWeight) {
416  tracksIter++;
417  continue;
418  }
419  // use original perigee parameter of course
420  BoundParameters trackPerigee =
421  m_extractParameters(*(tracksIter->originalParams));
422 
423  // compute compatibility
424  auto resultNew =
425  getCompatibility(trackPerigee, currentVertex, vertexingOptions);
426  if (!resultNew.ok()) {
427  return Result<bool>::failure(resultNew.error());
428  }
429  double chi2NewVtx = *resultNew;
430 
431  auto resultOld =
432  getCompatibility(trackPerigee, vertexIt, vertexingOptions);
433  if (!resultOld.ok()) {
434  return Result<bool>::failure(resultOld.error());
435  }
436  double chi2OldVtx = *resultOld;
437 
438  ACTS_DEBUG("Compatibility to new vertex: " << chi2NewVtx);
439  ACTS_DEBUG("Compatibility to old vertex: " << chi2OldVtx);
440 
441  if (chi2NewVtx < chi2OldVtx) {
442  perigeesToFit.push_back(tracksIter->originalParams);
443  // origTrack was already deleted from seedTracks previously
444  // (when assigned to old vertex)
445  // add it now back to seedTracks to be able to consistently
446  // delete it later
447  // when all tracks used to fit current vertex are deleted
448  seedTracks.push_back(tracksIter->originalParams);
449  // seedTracks.push_back(*std::find_if(
450  // origTracks.begin(), origTracks.end(),
451  // [&trackPerigee, this](auto origTrack) {
452  // return trackPerigee == m_extractParameters(*origTrack);
453  // }));
454 
455  numberOfAddedTracks += 1;
456 
457  // remove track from old vertex
458  tracksIter = tracksAtVertex.erase(tracksIter);
459  tracksBegin = tracksAtVertex.begin();
460  tracksEnd = tracksAtVertex.end();
461 
462  } // end chi2NewVtx < chi2OldVtx
463 
464  else {
465  // go and check next track
466  ++tracksIter;
467  }
468  } // end loop over tracks at old vertexIt
469 
470  vertexIt.setTracksAtVertex(tracksAtVertex);
471  } // end loop over all vertices
472 
473  ACTS_DEBUG("Added " << numberOfAddedTracks
474  << " tracks from old (other) vertices for new fit");
475 
476  // override current vertex with new fit
477  // set first to default vertex to be able to check if still good vertex
478  // later
479  currentVertex = Vertex<InputTrack_t>();
480  if (m_cfg.useBeamConstraint && !perigeesToFit.empty()) {
481  auto fitResult = m_cfg.vertexFitter.fit(perigeesToFit, m_cfg.linearizer,
482  vertexingOptions);
483  if (fitResult.ok()) {
484  currentVertex = std::move(*fitResult);
485  } else {
486  return Result<bool>::success(false);
487  }
488  } else if (!m_cfg.useBeamConstraint && perigeesToFit.size() > 1) {
489  auto fitResult = m_cfg.vertexFitter.fit(perigeesToFit, m_cfg.linearizer,
490  vertexingOptions);
491  if (fitResult.ok()) {
492  currentVertex = std::move(*fitResult);
493  } else {
494  return Result<bool>::success(false);
495  }
496  }
497 
498  // Number degrees of freedom
499  double ndf = currentVertex.fitQuality().second;
500 
501  // Number of significant tracks
502  int nTracksAtVertex = countSignificantTracks(currentVertex);
503 
504  bool isGoodVertex =
505  ((!m_cfg.useBeamConstraint && ndf > 0 && nTracksAtVertex >= 2) ||
506  (m_cfg.useBeamConstraint && ndf > 3 && nTracksAtVertex >= 2));
507 
508  if (!isGoodVertex) {
509  removeAllTracks(perigeesToFit, seedTracks);
510 
511  ACTS_DEBUG("Going to new iteration with "
512  << seedTracks.size() << "seed tracks after BAD vertex.");
513  }
514 
515  return Result<bool>::success(isGoodVertex);
516 }
517 
518 template <typename vfitter_t, typename sfinder_t>
520  const Vertex<InputTrack_t>& vtx) const {
521  return std::count_if(vtx.tracks().begin(), vtx.tracks().end(),
522  [this](TrackAtVertex<InputTrack_t> trk) {
523  return trk.trackWeight > m_cfg.cutOffTrackWeight;
524  });
525 }