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