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