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