EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AdaptiveMultiVertexFinder.ipp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file AdaptiveMultiVertexFinder.ipp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020 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 
10 
11 template <typename vfitter_t, typename sfinder_t>
13  const std::vector<const InputTrack_t*>& allTracks,
14  const VertexingOptions<InputTrack_t>& vertexingOptions,
15  State& /*state*/) const -> Result<std::vector<Vertex<InputTrack_t>>> {
16  if (allTracks.empty()) {
17  return VertexingError::EmptyInput;
18  }
19  // Original tracks
20  const std::vector<const InputTrack_t*>& origTracks = allTracks;
21 
22  // Seed tracks
23  std::vector<const InputTrack_t*> seedTracks = allTracks;
24 
25  FitterState_t fitterState(vertexingOptions.magFieldContext);
26  SeedFinderState_t seedFinderState;
27 
28  std::vector<std::unique_ptr<Vertex<InputTrack_t>>> allVertices;
29 
30  std::vector<Vertex<InputTrack_t>*> allVerticesPtr;
31 
32  int iteration = 0;
33  std::vector<const InputTrack_t*> removedSeedTracks;
34  while (((m_cfg.addSingleTrackVertices && seedTracks.size() > 0) ||
35  ((!m_cfg.addSingleTrackVertices) && seedTracks.size() > 1)) &&
36  iteration < m_cfg.maxIterations) {
37  // Tracks that are used for searching compatible tracks
38  // near a vertex candidate
39  std::vector<const InputTrack_t*> searchTracks;
40  if (m_cfg.doRealMultiVertex) {
41  searchTracks = origTracks;
42  } else {
43  searchTracks = seedTracks;
44  }
45  Vertex<InputTrack_t> currentConstraint = vertexingOptions.vertexConstraint;
46  // Retrieve seed vertex from all remaining seedTracks
47  auto seedResult = doSeeding(seedTracks, currentConstraint, vertexingOptions,
48  seedFinderState, removedSeedTracks);
49  if (!seedResult.ok()) {
50  return seedResult.error();
51  }
52  allVertices.push_back(std::make_unique<Vertex<InputTrack_t>>(*seedResult));
53 
54  Vertex<InputTrack_t>& vtxCandidate = *allVertices.back();
55  allVerticesPtr.push_back(&vtxCandidate);
56 
57  ACTS_DEBUG("Position of current vertex candidate after seeding: "
58  << vtxCandidate.fullPosition());
59  if (vtxCandidate.position().z() ==
60  vertexingOptions.vertexConstraint.position().z()) {
61  ACTS_DEBUG(
62  "No seed found anymore. Break and stop primary vertex finding.");
63  allVertices.pop_back();
64  allVerticesPtr.pop_back();
65  break;
66  }
67 
68  // Clear the seed track collection that has been removed in last iteration
69  // now after seed finding is done
70  removedSeedTracks.clear();
71 
72  auto prepResult = canPrepareVertexForFit(searchTracks, seedTracks,
73  vtxCandidate, currentConstraint,
74  fitterState, vertexingOptions);
75 
76  if (!prepResult.ok()) {
77  return prepResult.error();
78  }
79  if (!(*prepResult)) {
80  ACTS_DEBUG("Could not prepare for fit anymore. Break.");
81  allVertices.pop_back();
82  allVerticesPtr.pop_back();
83  break;
84  }
85  // Update fitter state with all vertices
86  fitterState.addVertexToMultiMap(vtxCandidate);
87 
88  // Perform the fit
89  auto fitResult = m_cfg.vertexFitter.addVtxToFit(
90  fitterState, vtxCandidate, m_cfg.linearizer, vertexingOptions);
91  if (!fitResult.ok()) {
92  return fitResult.error();
93  }
94  ACTS_DEBUG("New position of current vertex candidate after fit: "
95  << vtxCandidate.fullPosition());
96  // Check if vertex is good vertex
97  auto [nCompatibleTracks, isGoodVertex] =
98  checkVertexAndCompatibleTracks(vtxCandidate, seedTracks, fitterState);
99 
100  ACTS_DEBUG("Vertex is good vertex: " << isGoodVertex);
101  if (nCompatibleTracks > 0) {
102  removeCompatibleTracksFromSeedTracks(vtxCandidate, seedTracks,
103  fitterState, removedSeedTracks);
104  } else {
105  bool removedIncompatibleTrack = removeTrackIfIncompatible(
106  vtxCandidate, seedTracks, fitterState, removedSeedTracks,
107  vertexingOptions.geoContext);
108  if (!removedIncompatibleTrack) {
109  ACTS_DEBUG(
110  "Could not remove any further track from seed tracks. Break.");
111  allVertices.pop_back();
112  allVerticesPtr.pop_back();
113  break;
114  }
115  }
116  bool keepVertex = isGoodVertex &&
117  keepNewVertex(vtxCandidate, allVerticesPtr, fitterState);
118  ACTS_DEBUG("New vertex will be saved: " << keepVertex);
119 
120  // Delete vertex from allVertices list again if it's not kept
121  if (not keepVertex) {
122  auto deleteVertexResult =
123  deleteLastVertex(vtxCandidate, allVertices, allVerticesPtr,
124  fitterState, vertexingOptions);
125  if (not deleteVertexResult.ok()) {
126  return deleteVertexResult.error();
127  }
128  }
129  iteration++;
130  } // end while loop
131 
132  return getVertexOutputList(allVerticesPtr, fitterState);
133 }
134 
135 template <typename vfitter_t, typename sfinder_t>
137  const std::vector<const InputTrack_t*>& trackVector,
138  Vertex<InputTrack_t>& currentConstraint,
139  const VertexingOptions<InputTrack_t>& vertexingOptions,
140  SeedFinderState_t& seedFinderState,
141  const std::vector<const InputTrack_t*>& removedSeedTracks) const
143  VertexingOptions<InputTrack_t> seedOptions = vertexingOptions;
144  seedOptions.vertexConstraint = currentConstraint;
145 
147  seedFinderState.tracksToRemove = removedSeedTracks;
148  }
149 
150  // Run seed finder
151  auto seedResult =
152  m_cfg.seedFinder.find(trackVector, seedOptions, seedFinderState);
153 
154  if (!seedResult.ok()) {
155  return seedResult.error();
156  }
157 
158  Vertex<InputTrack_t> seedVertex = (*seedResult).back();
159  // Update constraints according to seed vertex
160  setConstraintAfterSeeding(currentConstraint, seedVertex);
161 
162  return seedVertex;
163 }
164 
165 template <typename vfitter_t, typename sfinder_t>
168  const Vertex<InputTrack_t>& seedVertex) const
169  -> void {
170  if (m_cfg.useBeamSpotConstraint) {
171  if (currentConstraint.fullCovariance() == SymMatrix4D::Zero()) {
172  ACTS_WARNING(
173  "No constraint provided, but useBeamSpotConstraint set to true.");
174  }
175  if (m_cfg.useSeedConstraint) {
176  currentConstraint.setFullPosition(seedVertex.fullPosition());
177  currentConstraint.setFullCovariance(seedVertex.fullCovariance());
178  }
179  } else {
180  currentConstraint.setFullPosition(seedVertex.fullPosition());
181  currentConstraint.setFullCovariance(SymMatrix4D::Identity() *
182  m_cfg.looseConstrValue);
183  currentConstraint.setFitQuality(m_cfg.defaultConstrFitQuality);
184  }
185 }
186 
187 template <typename vfitter_t, typename sfinder_t>
189  const InputTrack_t* track, const Vertex<InputTrack_t>& vtx,
190  const VertexingOptions<InputTrack_t>& vertexingOptions) const
191  -> Result<double> {
192  // TODO: In original implementation the covariance of the given vertex is set
193  // to zero. I did the same here now, but consider removing this and just
194  // passing the vtx object to the estimator without changing its covariance.
195  // After all, the vertex seed does have a non-zero convariance in general and
196  // it probably should be used.
197  Vertex<InputTrack_t> newVtx = vtx;
198  if (not m_cfg.useVertexCovForIPEstimation) {
199  newVtx.setFullCovariance(SymMatrix4D::Zero());
200  }
201 
202  auto estRes = m_cfg.ipEstimator.estimateImpactParameters(
203  m_extractParameters(*track), newVtx, vertexingOptions.geoContext,
204  vertexingOptions.magFieldContext);
205  if (!estRes.ok()) {
206  return estRes.error();
207  }
208 
209  ImpactParametersAndSigma ipas = *estRes;
210 
211  double significance = 0.;
212  if (ipas.sigmad0 > 0 && ipas.sigmaz0 > 0) {
213  significance = std::sqrt(std::pow(ipas.IPd0 / ipas.sigmad0, 2) +
214  std::pow(ipas.IPz0 / ipas.sigmaz0, 2));
215  }
216 
217  return significance;
218 }
219 
220 template <typename vfitter_t, typename sfinder_t>
223  const std::vector<const InputTrack_t*>& tracks,
224  Vertex<InputTrack_t>& vtx, FitterState_t& fitterState,
225  const VertexingOptions<InputTrack_t>& vertexingOptions) const
226  -> Result<void> {
227  for (const auto& trk : tracks) {
228  auto params = m_extractParameters(*trk);
229  auto pos = params.position(vertexingOptions.geoContext);
230  // If track is too far away from vertex, do not consider checking the IP
231  // significance
232  if (m_cfg.tracksMaxZinterval < std::abs(pos[eZ] - vtx.position()[eZ])) {
233  continue;
234  }
235  auto sigRes = getIPSignificance(trk, vtx, vertexingOptions);
236  if (!sigRes.ok()) {
237  return sigRes.error();
238  }
239  double ipSig = *sigRes;
240  if (ipSig < m_cfg.tracksMaxSignificance) {
241  // Create TrackAtVertex objects, unique for each (track, vertex) pair
242  fitterState.tracksAtVerticesMap.emplace(std::make_pair(trk, &vtx),
243  TrackAtVertex(params, trk));
244 
245  // Add the original track parameters to the list for vtx
246  fitterState.vtxInfoMap[&vtx].trackLinks.push_back(trk);
247  }
248  }
249  return {};
250 }
251 
252 template <typename vfitter_t, typename sfinder_t>
255  const std::vector<const InputTrack_t*>& allTracks,
256  const std::vector<const InputTrack_t*>& seedTracks,
258  const Vertex<InputTrack_t>& currentConstraint,
259  FitterState_t& fitterState,
260  const VertexingOptions<InputTrack_t>& vertexingOptions) const
261  -> Result<bool> {
262  // Recover from cases where no compatible tracks to vertex
263  // candidate were found
264  // TODO: This is for now how it's done in athena... this look a bit
265  // nasty to me
266  if (fitterState.vtxInfoMap[&vtx].trackLinks.empty()) {
267  // Find nearest track to vertex candidate
268  double smallestDeltaZ = std::numeric_limits<double>::max();
269  double newZ = 0;
270  bool nearTrackFound = false;
271  for (const auto& trk : seedTracks) {
272  auto pos =
273  m_extractParameters(*trk).position(vertexingOptions.geoContext);
274  auto zDistance = std::abs(pos[eZ] - vtx.position()[eZ]);
275  if (zDistance < smallestDeltaZ) {
276  smallestDeltaZ = zDistance;
277  nearTrackFound = true;
278  newZ = pos[eZ];
279  }
280  }
281  if (nearTrackFound) {
282  vtx.setFullPosition(Vector4D(0., 0., newZ, 0.));
283 
284  // Update vertex info for current vertex
285  fitterState.vtxInfoMap[&vtx] =
286  VertexInfo<InputTrack_t>(currentConstraint, vtx.fullPosition());
287 
288  // Try to add compatible track with adapted vertex position
289  auto res = addCompatibleTracksToVertex(allTracks, vtx, fitterState,
290  vertexingOptions);
291  if (!res.ok()) {
292  return Result<bool>::failure(res.error());
293  }
294 
295  if (fitterState.vtxInfoMap[&vtx].trackLinks.empty()) {
296  ACTS_DEBUG(
297  "No tracks near seed were found, while at least one was "
298  "expected. Break.");
299  return Result<bool>::success(false);
300  }
301 
302  } else {
303  ACTS_DEBUG("No nearest track to seed found. Break.");
304  return Result<bool>::success(false);
305  }
306  }
307 
308  return Result<bool>::success(true);
309 }
310 
311 template <typename vfitter_t, typename sfinder_t>
314  const std::vector<const InputTrack_t*>& allTracks,
315  const std::vector<const InputTrack_t*>& seedTracks,
317  const Vertex<InputTrack_t>& currentConstraint,
318  FitterState_t& fitterState,
319  const VertexingOptions<InputTrack_t>& vertexingOptions) const
320  -> Result<bool> {
321  // Add vertex info to fitter state
322  fitterState.vtxInfoMap[&vtx] =
323  VertexInfo<InputTrack_t>(currentConstraint, vtx.fullPosition());
324 
325  // Add all compatible tracks to vertex
326  auto resComp = addCompatibleTracksToVertex(allTracks, vtx, fitterState,
327  vertexingOptions);
328  if (!resComp.ok()) {
329  return Result<bool>::failure(resComp.error());
330  }
331 
332  // Try to recover from cases where adding compatible track was not possible
333  auto resRec = canRecoverFromNoCompatibleTracks(allTracks, seedTracks, vtx,
334  currentConstraint, fitterState,
335  vertexingOptions);
336  if (!resRec.ok()) {
337  return Result<bool>::failure(resRec.error());
338  }
339 
340  return Result<bool>::success(*resRec);
341 }
342 
343 template <typename vfitter_t, typename sfinder_t>
347  const std::vector<const InputTrack_t*>& seedTracks,
348  FitterState_t& fitterState) const -> std::pair<int, bool> {
349  bool isGoodVertex = false;
350  int nCompatibleTracks = 0;
351  for (const auto& trk : fitterState.vtxInfoMap[&vtx].trackLinks) {
352  const auto& trkAtVtx =
353  fitterState.tracksAtVerticesMap.at(std::make_pair(trk, &vtx));
354  if ((trkAtVtx.vertexCompatibility < m_cfg.maxVertexChi2 &&
355  m_cfg.useFastCompatibility) ||
356  (trkAtVtx.trackWeight > m_cfg.minWeight &&
357  trkAtVtx.chi2Track < m_cfg.maxVertexChi2 &&
358  !m_cfg.useFastCompatibility)) {
359  // TODO: Understand why looking for compatible tracks only in seed tracks
360  // and not also in all tracks
361  auto foundIter =
362  std::find_if(seedTracks.begin(), seedTracks.end(),
363  [&trk](auto seedTrk) { return trk == seedTrk; });
364  if (foundIter != seedTracks.end()) {
365  nCompatibleTracks++;
366  ACTS_DEBUG("Compatible track found.");
367 
368  if (m_cfg.addSingleTrackVertices && m_cfg.useBeamSpotConstraint) {
369  isGoodVertex = true;
370  break;
371  }
372  if (nCompatibleTracks > 1) {
373  isGoodVertex = true;
374  break;
375  }
376  }
377  }
378  } // end loop over all tracks at vertex
379 
380  return {nCompatibleTracks, isGoodVertex};
381 }
382 
383 template <typename vfitter_t, typename sfinder_t>
386  Vertex<InputTrack_t>& vtx, std::vector<const InputTrack_t*>& seedTracks,
387  FitterState_t& fitterState,
388  std::vector<const InputTrack_t*>& removedSeedTracks) const -> void {
389  for (const auto& trk : fitterState.vtxInfoMap[&vtx].trackLinks) {
390  const auto& trkAtVtx =
391  fitterState.tracksAtVerticesMap.at(std::make_pair(trk, &vtx));
392  if ((trkAtVtx.vertexCompatibility < m_cfg.maxVertexChi2 &&
393  m_cfg.useFastCompatibility) ||
394  (trkAtVtx.trackWeight > m_cfg.minWeight &&
395  trkAtVtx.chi2Track < m_cfg.maxVertexChi2 &&
396  !m_cfg.useFastCompatibility)) {
397  // Find and remove track from seedTracks
398  auto foundSeedIter =
399  std::find_if(seedTracks.begin(), seedTracks.end(),
400  [&trk](auto seedTrk) { return trk == seedTrk; });
401  if (foundSeedIter != seedTracks.end()) {
402  seedTracks.erase(foundSeedIter);
403  removedSeedTracks.push_back(trk);
404  }
405  }
406  }
407 }
408 
409 template <typename vfitter_t, typename sfinder_t>
412  Vertex<InputTrack_t>& vtx, std::vector<const InputTrack_t*>& seedTracks,
413  FitterState_t& fitterState,
414  std::vector<const InputTrack_t*>& removedSeedTracks,
415  const GeometryContext& geoCtx) const -> bool {
416  // Try to find the track with highest compatibility
417  double maxCompatibility = 0;
418 
419  auto maxCompSeedIt = seedTracks.end();
420  const InputTrack_t* removedTrack = nullptr;
421  for (const auto& trk : fitterState.vtxInfoMap[&vtx].trackLinks) {
422  const auto& trkAtVtx =
423  fitterState.tracksAtVerticesMap.at(std::make_pair(trk, &vtx));
424  double compatibility = trkAtVtx.vertexCompatibility;
425  if (compatibility > maxCompatibility) {
426  // Try to find track in seed tracks
427  auto foundSeedIter =
428  std::find_if(seedTracks.begin(), seedTracks.end(),
429  [&trk](auto seedTrk) { return trk == seedTrk; });
430  if (foundSeedIter != seedTracks.end()) {
431  maxCompatibility = compatibility;
432  maxCompSeedIt = foundSeedIter;
433  removedTrack = trk;
434  }
435  }
436  }
437  if (maxCompSeedIt != seedTracks.end()) {
438  // Remove track with highest compatibility from seed tracks
439  seedTracks.erase(maxCompSeedIt);
440  removedSeedTracks.push_back(removedTrack);
441  } else {
442  // Could not find any seed with compatibility > 0, use alternative
443  // method to remove a track from seed tracks: Closest track in z to
444  // vtx candidate
445  double smallestDeltaZ = std::numeric_limits<double>::max();
446  auto smallestDzSeedIter = seedTracks.end();
447  for (unsigned int i = 0; i < seedTracks.size(); i++) {
448  auto pos = m_extractParameters(*seedTracks[i]).position(geoCtx);
449  double zDistance = std::abs(pos[eZ] - vtx.position()[eZ]);
450  if (zDistance < smallestDeltaZ) {
451  smallestDeltaZ = zDistance;
452  smallestDzSeedIter = seedTracks.begin() + i;
453  removedTrack = seedTracks[i];
454  }
455  }
456  if (smallestDzSeedIter != seedTracks.end()) {
457  seedTracks.erase(smallestDzSeedIter);
458  removedSeedTracks.push_back(removedTrack);
459  } else {
460  ACTS_DEBUG("No track found to remove. Stop vertex finding now.");
461  return false;
462  }
463  }
464  return true;
465 }
466 
467 template <typename vfitter_t, typename sfinder_t>
470  const std::vector<Vertex<InputTrack_t>*>& allVertices,
471  FitterState_t& fitterState) const -> bool {
472  double contamination = 0.;
473  double contaminationNum = 0;
474  double contaminationDeNom = 0;
475  for (const auto& trk : fitterState.vtxInfoMap[&vtx].trackLinks) {
476  const auto& trkAtVtx =
477  fitterState.tracksAtVerticesMap.at(std::make_pair(trk, &vtx));
478  double trackWeight = trkAtVtx.trackWeight;
479  contaminationNum += trackWeight * (1. - trackWeight);
480  contaminationDeNom += trackWeight * trackWeight;
481  }
482  if (contaminationDeNom != 0) {
483  contamination = contaminationNum / contaminationDeNom;
484  }
485  if (contamination > m_cfg.maximumVertexContamination) {
486  return false;
487  }
488 
489  if (isMergedVertex(vtx, allVertices)) {
490  return false;
491  }
492 
493  return true;
494 }
495 
496 template <typename vfitter_t, typename sfinder_t>
498  const Vertex<InputTrack_t>& vtx,
499  const std::vector<Vertex<InputTrack_t>*>& allVertices) const -> bool {
500  const Vector4D& candidatePos = vtx.fullPosition();
501  const SymMatrix4D& candidateCov = vtx.fullCovariance();
502  const double candidateZPos = candidatePos[eZ];
503  const double candidateZCov = candidateCov(eZ, eZ);
504 
505  for (const auto otherVtx : allVertices) {
506  if (&vtx == otherVtx) {
507  continue;
508  }
509  const Vector4D& otherPos = otherVtx->fullPosition();
510  const SymMatrix4D& otherCov = otherVtx->fullCovariance();
511  const double otherZPos = otherPos[eZ];
512  const double otherZCov = otherCov(eZ, eZ);
513 
514  const Vector4D deltaPos = otherPos - candidatePos;
515  const double deltaZPos = otherZPos - candidateZPos;
516  const double sumCovZ = otherZCov + candidateZCov;
517 
518  double significance;
519  if (not m_cfg.do3dSplitting) {
520  // Use only z significance
521  if (sumCovZ > 0.) {
522  significance = std::abs(deltaZPos) / std::sqrt(sumCovZ);
523  } else {
524  return true;
525  }
526  } else {
527  // Use full 3d information for significance
528  SymMatrix4D sumCov = candidateCov + otherCov;
529  significance =
530  std::sqrt(deltaPos.dot((sumCov.inverse().eval()) * deltaPos));
531  }
532  if (significance < m_cfg.maxMergeVertexSignificance) {
533  return true;
534  }
535  }
536  return false;
537 }
538 
539 template <typename vfitter_t, typename sfinder_t>
542  std::vector<std::unique_ptr<Vertex<InputTrack_t>>>& allVertices,
543  std::vector<Vertex<InputTrack_t>*>& allVerticesPtr,
544  FitterState_t& fitterState,
545  const VertexingOptions<InputTrack_t>& vertexingOptions) const
546  -> Result<void> {
547  allVertices.pop_back();
548  allVerticesPtr.pop_back();
549 
550  // Update fitter state with removed vertex candidate
551  fitterState.removeVertexFromMultiMap(vtx);
552 
553  // Do the fit with removed vertex
554  auto fitResult = m_cfg.vertexFitter.addVtxToFit(
555  fitterState, vtx, m_cfg.linearizer, vertexingOptions);
556  if (!fitResult.ok()) {
557  return fitResult.error();
558  }
559 
560  return {};
561 }
562 
563 template <typename vfitter_t, typename sfinder_t>
565  const std::vector<Vertex<InputTrack_t>*>& allVerticesPtr,
566  FitterState_t& fitterState) const
568  std::vector<Vertex<InputTrack_t>> outputVec;
569  for (auto vtx : allVerticesPtr) {
570  auto& outVtx = *vtx;
571  std::vector<TrackAtVertex<InputTrack_t>> tracksAtVtx;
572  for (const auto& trk : fitterState.vtxInfoMap[vtx].trackLinks) {
573  tracksAtVtx.push_back(
574  fitterState.tracksAtVerticesMap.at(std::make_pair(trk, vtx)));
575  }
576  outVtx.setTracksAtVertex(tracksAtVtx);
577  outputVec.push_back(outVtx);
578  }
579  return Result<std::vector<Vertex<InputTrack_t>>>(outputVec);
580 }