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