Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SingleSeedVertexFinder.ipp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SingleSeedVertexFinder.ipp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2023 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 #include <cmath>
10 #include <system_error>
11 
12 #include <Eigen/Eigenvalues>
13 
14 template <typename spacepoint_t>
17  std::unique_ptr<const Logger> lgr)
18  : m_cfg(cfg), m_logger(std::move(lgr)) {
19  if (cfg.numPhiSlices < 3) {
20  ACTS_INFO("value of numPhiSlices is "
21  << cfg.numPhiSlices
22  << ", which is less than 3. There will be duplicate triplets.");
23  }
24  if (cfg.useFracPhiSlices <= 0. || cfg.useFracPhiSlices > 1.) {
25  ACTS_ERROR("value of useFracPhiSlices is "
26  << cfg.useFracPhiSlices
27  << ", allowed values are between 0 and 1");
28  }
29  if (cfg.useFracZSlices <= 0. || cfg.useFracZSlices > 1.) {
30  ACTS_ERROR("value of useFracZSlices is "
31  << cfg.useFracZSlices << ", allowed values are between 0 and 1");
32  }
33  if (cfg.minimalizeWRT != "planes" && cfg.minimalizeWRT != "rays") {
34  ACTS_ERROR("value of minimalizeWRT is "
35  << cfg.minimalizeWRT
36  << ", allowed values are \"planes\" or \"rays\" ");
37  }
38  if (cfg.removeFraction < 0. || cfg.removeFraction >= 1.) {
39  ACTS_ERROR("value of removeFraction is "
40  << cfg.removeFraction << ", allowed values are between 0 and 1");
41  }
42 }
43 
44 template <typename spacepoint_t>
47  const std::vector<spacepoint_t>& spacepoints) const {
48  // sort spacepoints to different phi and z slices
50  sortedSpacepoints = sortSpacepoints(spacepoints);
51 
52  // find triplets
53  std::vector<Acts::SingleSeedVertexFinder<spacepoint_t>::Triplet> triplets =
54  findTriplets(sortedSpacepoints);
55 
56  // if no valid triplets found
57  if (triplets.empty()) {
58  return Acts::Result<Acts::Vector3>::failure(std::error_code());
59  }
60 
61  Acts::Vector3 vtx = Acts::Vector3::Zero();
62  if (m_cfg.minimalizeWRT == "planes") {
63  // find a point closest to all planes defined by the triplets
64  vtx = findClosestPointFromPlanes(triplets);
65  } else if (m_cfg.minimalizeWRT == "rays") {
66  // find a point closest to all rays fitted through the triplets
67  vtx = findClosestPointFromRays(triplets);
68  } else {
69  ACTS_ERROR("value of minimalizeWRT is "
70  << m_cfg.minimalizeWRT
71  << ", allowed values are \"planes\" or \"rays\" ");
72  }
73 
75 }
76 
77 template <typename spacepoint_t>
80  const std::vector<spacepoint_t>& spacepoints) const {
82  sortedSpacepoints(m_cfg.numPhiSlices, m_cfg.numZSlices);
83 
84  for (const auto& sp : spacepoints) {
85  // phi will be saved for later
86  Acts::ActsScalar phi = detail::radian_pos(std::atan2(sp.y(), sp.x()));
87  std::uint32_t phislice =
88  (std::uint32_t)(phi / (2 * M_PI) * m_cfg.numPhiSlices);
89  if (phislice >= m_cfg.numPhiSlices) {
90  phislice = 0;
91  }
92 
93  if (std::abs(sp.z()) >= m_cfg.maxAbsZ) {
94  continue;
95  }
96  std::uint32_t zslice = (std::uint32_t)(
97  (sp.z() + m_cfg.maxAbsZ) / (2 * m_cfg.maxAbsZ) * m_cfg.numZSlices);
98 
99  // input spacepoint is sorted into one subset
100  if (sp.r() < m_cfg.rMinMiddle) {
101  if (m_cfg.rMinNear < sp.r() && sp.r() < m_cfg.rMaxNear) {
102  if (std::fmod(m_cfg.useFracPhiSlices * phislice, 1.0) >=
103  m_cfg.useFracPhiSlices) {
104  continue;
105  }
106  sortedSpacepoints.addSP(0, phislice, zslice)
107  .emplace_back((spacepoint_t const*)&sp, phi);
108  }
109  } else if (sp.r() < m_cfg.rMinFar) {
110  if (sp.r() < m_cfg.rMaxMiddle) {
111  if (std::fmod(m_cfg.useFracZSlices * zslice, 1.0) >=
112  m_cfg.useFracZSlices) {
113  continue;
114  }
115  sortedSpacepoints.addSP(1, phislice, zslice)
116  .emplace_back((spacepoint_t const*)&sp, phi);
117  }
118  } else if (sp.r() < m_cfg.rMaxFar) {
119  sortedSpacepoints.addSP(2, phislice, zslice)
120  .emplace_back((spacepoint_t const*)&sp, phi);
121  }
122  }
123 
124  return sortedSpacepoints;
125 }
126 
127 template <typename spacepoint_t>
128 std::vector<typename Acts::SingleSeedVertexFinder<spacepoint_t>::Triplet>
131  sortedSpacepoints) const {
132  std::vector<Acts::SingleSeedVertexFinder<spacepoint_t>::Triplet> triplets;
133 
134  std::uint32_t phiStep =
135  (std::uint32_t)(m_cfg.maxPhideviation / (2 * M_PI / m_cfg.numPhiSlices)) +
136  1;
137 
138  // calculate limits for middle spacepoints
139  Acts::Vector2 vecA{-m_cfg.maxAbsZ + m_cfg.maxZPosition, m_cfg.rMinFar};
140  vecA /= 2.;
141  Acts::Vector2 vecB = {vecA[1], -vecA[0]};
142  vecB /= std::tan(m_cfg.maxXYZdeviation);
143  Acts::Vector2 posR = Acts::Vector2(-m_cfg.maxZPosition, 0.) + vecA + vecB;
144  Acts::ActsScalar R = vecA.norm() / std::sin(m_cfg.maxXYZdeviation);
145  Acts::ActsScalar constB = -2. * posR[0];
146  Acts::ActsScalar constC =
147  posR[0] * posR[0] +
148  (posR[1] - m_cfg.rMaxNear) * (posR[1] - m_cfg.rMaxNear) - R * R;
149  Acts::ActsScalar maxZMiddle =
150  -1. * (-constB - sqrt(constB * constB - 4. * constC)) / 2.;
151  if (maxZMiddle <= 0) {
152  ACTS_WARNING(
153  "maximum position of middle spacepoints is not positive, maxZMiddle = "
154  << maxZMiddle << ", check your config; setting maxZMiddle to "
155  << m_cfg.maxAbsZ);
156  maxZMiddle = m_cfg.maxAbsZ;
157  }
158 
159  // save some constant values for later
160  Acts::ActsScalar rNearRatio[2] = {m_cfg.rMinNear / m_cfg.rMaxMiddle,
161  m_cfg.rMaxNear / m_cfg.rMinMiddle};
162  Acts::ActsScalar rMiddle[2] = {m_cfg.rMaxMiddle, m_cfg.rMinMiddle};
163  Acts::ActsScalar rFarDelta[2] = {
164  m_cfg.rMaxFar - m_cfg.rMinMiddle,
165  m_cfg.rMinFar - m_cfg.rMaxMiddle,
166  };
167  Acts::ActsScalar zBinLength = 2. * m_cfg.maxAbsZ / m_cfg.numZSlices;
168 
169  // limits in terms of slice numbers
170  std::uint32_t limitMiddleSliceFrom =
171  (std::uint32_t)((-maxZMiddle + m_cfg.maxAbsZ) / zBinLength);
172  std::uint32_t limitMiddleSliceTo =
173  (std::uint32_t)((maxZMiddle + m_cfg.maxAbsZ) / zBinLength + 1);
174  std::uint32_t limitAbsZSliceFrom = (std::uint32_t)(
175  (-m_cfg.maxZPosition + m_cfg.maxAbsZ) / zBinLength + 0.01);
176  std::uint32_t limitAbsZSliceTo =
177  (std::uint32_t)((m_cfg.maxZPosition + m_cfg.maxAbsZ) / zBinLength + 1.01);
178 
179  for (std::uint32_t middleZ = limitMiddleSliceFrom;
180  middleZ < limitMiddleSliceTo; ++middleZ) {
181  // skip slices that are empty anyway
182  if (std::fmod(m_cfg.useFracZSlices * middleZ, 1.0) >=
183  m_cfg.useFracZSlices) {
184  continue;
185  }
186 
187  // calculate limits for near spacepoints, assuming the middle spacepoints
188  // are within some boundaries
189  bool isLessFrom = (middleZ <= limitAbsZSliceFrom);
190  Acts::ActsScalar deltaZfrom =
191  (middleZ - limitAbsZSliceFrom - 1) * zBinLength;
192  Acts::ActsScalar angleZfrom =
193  std::atan2(rMiddle[isLessFrom], deltaZfrom) + m_cfg.maxXYZdeviation;
194  std::uint32_t nearZFrom = 0;
195  if (angleZfrom < M_PI) {
196  Acts::ActsScalar new_deltaZfrom =
197  rMiddle[isLessFrom] / std::tan(angleZfrom) / zBinLength;
198  nearZFrom = (std::uint32_t)std::max(
199  new_deltaZfrom * rNearRatio[isLessFrom] + limitAbsZSliceFrom, 0.);
200  }
201 
202  bool isLessTo = (middleZ < limitAbsZSliceTo);
203  Acts::ActsScalar deltaZto = (middleZ - limitAbsZSliceTo + 1) * zBinLength;
204  Acts::ActsScalar angleZto =
205  std::atan2(rMiddle[!isLessTo], deltaZto) - m_cfg.maxXYZdeviation;
206  std::uint32_t nearZTo = m_cfg.numZSlices;
207  if (angleZto > 0) {
208  Acts::ActsScalar new_deltaZto =
209  rMiddle[!isLessTo] / std::tan(angleZto) / zBinLength;
210  nearZTo = (std::uint32_t)std::max(
211  new_deltaZto * rNearRatio[!isLessTo] + limitAbsZSliceTo, 0.);
212  if (nearZTo > m_cfg.numZSlices) {
213  nearZTo = m_cfg.numZSlices;
214  }
215  }
216 
217  for (std::uint32_t nearZ = nearZFrom; nearZ < nearZTo; ++nearZ) {
218  // calculate limits for far spacepoits, assuming middle and near
219  // spacepoits are within some boundaries
220  bool isMiddleLess = (middleZ <= nearZ);
221 
222  Acts::ActsScalar delta2Zfrom = (middleZ - nearZ - 1) * zBinLength;
223  Acts::ActsScalar angle2Zfrom =
224  std::atan2(rFarDelta[isMiddleLess], delta2Zfrom) +
225  m_cfg.maxXYZdeviation;
226  std::uint32_t farZFrom = 0;
227  if (angle2Zfrom < M_PI) {
228  farZFrom = (std::uint32_t)std::max(
229  (rFarDelta[isMiddleLess] / std::tan(angle2Zfrom) / zBinLength) +
230  middleZ,
231  0.);
232  if (farZFrom >= m_cfg.numZSlices) {
233  continue;
234  }
235  }
236 
237  isMiddleLess = (middleZ < nearZ);
238  Acts::ActsScalar delta2Zto = (middleZ - nearZ + 1) * zBinLength;
239  Acts::ActsScalar angle2Zto =
240  std::atan2(rFarDelta[!isMiddleLess], delta2Zto) -
241  m_cfg.maxXYZdeviation;
242  std::uint32_t farZTo = m_cfg.numZSlices;
243  if (angle2Zto > 0) {
244  farZTo = (std::uint32_t)std::max(
245  (rFarDelta[!isMiddleLess] / std::tan(angle2Zto) / zBinLength) +
246  middleZ + 1,
247  0.);
248  if (farZTo > m_cfg.numZSlices) {
249  farZTo = m_cfg.numZSlices;
250  } else if (farZTo == 0) {
251  continue;
252  }
253  }
254 
255  for (std::uint32_t farZ = farZFrom; farZ < farZTo; farZ++) {
256  // loop over near phi slices
257  for (std::uint32_t nearPhi = 0; nearPhi < m_cfg.numPhiSlices;
258  ++nearPhi) {
259  // skip slices that are empty anyway
260  if (std::fmod(m_cfg.useFracPhiSlices * nearPhi, 1.0) >=
261  m_cfg.useFracPhiSlices) {
262  continue;
263  }
264 
265  // loop over some middle phi slices
266  for (std::uint32_t middlePhi_h =
267  m_cfg.numPhiSlices + nearPhi - phiStep;
268  middlePhi_h <= m_cfg.numPhiSlices + nearPhi + phiStep;
269  ++middlePhi_h) {
270  std::uint32_t middlePhi = middlePhi_h % m_cfg.numPhiSlices;
271 
272  // loop over some far phi slices
273  for (std::uint32_t farPhi_h =
274  m_cfg.numPhiSlices + middlePhi - phiStep;
275  farPhi_h <= m_cfg.numPhiSlices + middlePhi + phiStep;
276  ++farPhi_h) {
277  std::uint32_t farPhi = farPhi_h % m_cfg.numPhiSlices;
278 
279  // for all near spacepoints in this slice
280  for (const auto& nearSP :
281  sortedSpacepoints.getSP(0, nearPhi, nearZ)) {
282  Acts::ActsScalar phiA = nearSP.second;
283 
284  // for all middle spacepoints in this slice
285  for (const auto& middleSP :
286  sortedSpacepoints.getSP(1, middlePhi, middleZ)) {
287  Acts::ActsScalar phiB = middleSP.second;
288  Acts::ActsScalar deltaPhiAB =
289  detail::difference_periodic(phiA, phiB, 2 * M_PI);
290  if (std::abs(deltaPhiAB) > m_cfg.maxPhideviation) {
291  continue;
292  }
293 
294  // for all far spacepoints in this slice
295  for (const auto& farSP :
296  sortedSpacepoints.getSP(2, farPhi, farZ)) {
297  Acts::ActsScalar phiC = farSP.second;
298  Acts::ActsScalar deltaPhiBC =
299  detail::difference_periodic(phiB, phiC, 2 * M_PI);
300  if (std::abs(deltaPhiBC) > m_cfg.maxPhideviation) {
301  continue;
302  }
303 
305  *nearSP.first, *middleSP.first, *farSP.first);
306 
307  if (tripletValidationAndUpdate(tr)) {
308  triplets.push_back(tr);
309  }
310  } // loop over far spacepoints
311  } // loop over middle spacepoints
312  } // loop over near spacepoints
313  } // loop over far phi slices
314  } // loop over middle phi slices
315  } // loop over near phi slices
316  } // loop over far Z slices
317  } // loop over near Z slices
318  } // loop over middle Z slices
319 
320  return triplets;
321 }
322 
323 template <typename spacepoint_t>
326  // slope for near+middle spacepoints
327  Acts::ActsScalar alpha1 =
328  std::atan2(triplet.a.y() - triplet.b.y(), triplet.a.x() - triplet.b.x());
329  // slope for middle+far spacepoints
330  Acts::ActsScalar alpha2 =
331  std::atan2(triplet.b.y() - triplet.c.y(), triplet.b.x() - triplet.c.x());
332  // these two slopes shouldn't be too different
333  Acts::ActsScalar deltaAlpha =
334  detail::difference_periodic(alpha1, alpha2, 2 * M_PI);
335  if (std::abs(deltaAlpha) > m_cfg.maxXYdeviation) {
336  return false;
337  }
338 
339  // near-middle ray
340  Acts::Vector3 ab{triplet.a.x() - triplet.b.x(), triplet.a.y() - triplet.b.y(),
341  triplet.a.z() - triplet.b.z()};
342  // middle-far ray
343  Acts::Vector3 bc{triplet.b.x() - triplet.c.x(), triplet.b.y() - triplet.c.y(),
344  triplet.b.z() - triplet.c.z()};
345  // dot product of these two
346  Acts::ActsScalar cosTheta = (ab.dot(bc)) / (ab.norm() * bc.norm());
347  Acts::ActsScalar theta = std::acos(cosTheta);
348  if (theta > m_cfg.maxXYZdeviation) {
349  return false;
350  }
351 
352  // reject the ray if it doesn't come close to the z-axis
353  Acts::Ray3D ray = makeRayFromTriplet(triplet);
354  const Acts::Vector3& startPoint = ray.origin();
355  const Acts::Vector3& direction = ray.dir();
356  // norm to z-axis and to the ray
357  Acts::Vector3 norm{-1. * direction[1], 1. * direction[0], 0};
358  Acts::ActsScalar norm_size = norm.norm();
359 
360  Acts::ActsScalar tanTheta = norm_size / direction[2];
361  if (std::abs(tanTheta) < std::tan(m_cfg.minTheta)) {
362  return false;
363  }
364 
365  // nearest distance from the ray to z-axis
366  Acts::ActsScalar dist = std::abs(startPoint.dot(norm)) / norm_size;
367  if (dist > m_cfg.maxRPosition) {
368  return false;
369  }
370 
371  // z coordinate of the nearest distance from the ray to z-axis
372  Acts::ActsScalar zDist =
373  direction.cross(norm).dot(startPoint) / (norm_size * norm_size);
374  if (std::abs(zDist) > m_cfg.maxZPosition) {
375  return false;
376  }
377 
378  if (m_cfg.minimalizeWRT == "rays") {
379  // save for later
380  triplet.ray = ray;
381  }
382 
383  return true;
384 }
385 
386 template <typename spacepoint_t>
387 std::pair<Acts::Vector3, Acts::ActsScalar>
390  Acts::Vector3 a{triplet.a.x(), triplet.a.y(), triplet.a.z()};
391  Acts::Vector3 b{triplet.b.x(), triplet.b.y(), triplet.b.z()};
392  Acts::Vector3 c{triplet.c.x(), triplet.c.y(), triplet.c.z()};
393 
394  Acts::Vector3 ba = b - a, ca = c - a;
395 
396  // vector (alpha,beta,gamma) normalized to unity for convenience
397  Acts::Vector3 abg = ba.cross(ca).normalized();
398  Acts::ActsScalar delta = -1. * abg.dot(a);
399 
400  // plane (alpha*x + beta*y + gamma*z + delta = 0), split to {{alpha, beta,
401  // gamma}, delta} for convenience
402  return {abg, delta};
403 }
404 
405 template <typename spacepoint_t>
409  triplets) const {
410  // 1. define function f = sum over all triplets [distance from an unknown
411  // point
412  // (x_0,y_0,z_0) to the plane defined by the triplet]
413  // 2. find minimum of "f" by partial derivations over x_0, y_0, and z_0
414  // 3. each derivation has parts linearly depending on x_0, y_0, and z_0
415  // (will fill A[deriv][3]) or to nothing (will fill B[deriv])
416  // 4. solve A*(x_0,y_0,z_0) = B
417 
418  Acts::Vector3 vtx = Acts::Vector3::Zero();
419  Acts::Vector3 vtxPrev{m_cfg.rMaxFar, m_cfg.rMaxFar, m_cfg.maxAbsZ};
420 
421  // (alpha-beta-gamma, delta), distance
422  std::vector<
423  std::pair<std::pair<Acts::Vector3, Acts::ActsScalar>, Acts::ActsScalar>>
424  tripletsWithPlanes;
425  tripletsWithPlanes.reserve(triplets.size());
426 
427  for (const auto& triplet : triplets) {
428  auto abgd = makePlaneFromTriplet(triplet);
429  tripletsWithPlanes.emplace_back(abgd, -1.);
430  }
431 
432  // elements of the linear equations to solve
433  Acts::SquareMatrix3 A = Acts::SquareMatrix3::Zero();
434  Acts::Vector3 B = Acts::Vector3::Zero();
435  for (const auto& triplet : tripletsWithPlanes) {
436  const auto& abg = triplet.first.first;
437  const auto& delta = triplet.first.second;
438 
439  A += 2. * (abg * abg.transpose());
440  B -= 2. * delta * abg;
441  }
442 
443  for (std::uint32_t iter = 0; iter <= m_cfg.maxIterations; iter++) {
444  // new vertex position
445  vtx = A.lu().solve(B);
446 
447  Acts::Vector3 vtxDiff = vtx - vtxPrev;
448 
449  if (vtxDiff.norm() < m_cfg.minVtxShift) {
450  // difference between the new vertex and the old vertex is not so large
451  break;
452  }
453 
454  if (iter != m_cfg.maxIterations) {
455  // is not the last iteration
456  vtxPrev = vtx;
457 
458  for (auto& triplet : tripletsWithPlanes) {
459  const auto& abg = triplet.first.first;
460  const auto& delta = triplet.first.second;
461  Acts::ActsScalar distance = std::abs(abg.dot(vtx) + delta);
462  triplet.second = distance;
463  }
464 
465  std::sort(tripletsWithPlanes.begin(), tripletsWithPlanes.end(),
466  [](const auto& lhs, const auto& rhs) {
467  return lhs.second < rhs.second;
468  });
469 
470  std::uint32_t threshold = (std::uint32_t)(tripletsWithPlanes.size() *
471  (1. - m_cfg.removeFraction));
472 
473  for (std::uint32_t tr = threshold + 1; tr < tripletsWithPlanes.size();
474  ++tr) {
475  const auto& abg = tripletsWithPlanes[tr].first.first;
476  const auto& delta = tripletsWithPlanes[tr].first.second;
477 
478  // remove this triplet from A and B
479  A -= 2. * (abg * abg.transpose());
480  B += 2. * delta * abg;
481  }
482 
483  // remove all excessive triplets
484  tripletsWithPlanes.resize(threshold);
485  }
486  }
487 
488  return vtx;
489 }
490 
491 template <typename spacepoint_t>
495  mat.row(0) = Acts::Vector3(triplet.a.x(), triplet.a.y(), triplet.a.z());
496  mat.row(1) = Acts::Vector3(triplet.b.x(), triplet.b.y(), triplet.b.z());
497  mat.row(2) = Acts::Vector3(triplet.c.x(), triplet.c.y(), triplet.c.z());
498 
499  Acts::Vector3 mean = mat.colwise().mean();
500  Acts::SquareMatrix3 cov = (mat.rowwise() - mean.transpose()).transpose() *
501  (mat.rowwise() - mean.transpose()) / 3.;
502 
503  // "cov" is self-adjoint matrix
504  Eigen::SelfAdjointEigenSolver<Acts::SquareMatrix3> saes(cov);
505  // eigenvalues are sorted in increasing order
506  Acts::Vector3 eivec = saes.eigenvectors().col(2);
507 
508  return {mean, eivec};
509 }
510 
511 template <typename spacepoint_t>
515  triplets) const {
516  // 1. define function f = sum over all triplets [distance from an unknown
517  // point
518  // (x_0,y_0,z_0) to the ray defined by the triplet]
519  // 2. find minimum of "f" by partial derivations over x_0, y_0, and z_0
520  // 3. each derivation has parts linearly depending on x_0, y_0, and z_0
521  // (will fill A[][3]) or to nothing (will fill B[])
522  // 4. solve A*(x_0,y_0,z_0) = B
523 
524  Acts::Vector3 vtx = Acts::Vector3::Zero();
525  Acts::Vector3 vtxPrev{m_cfg.rMaxFar, m_cfg.rMaxFar, m_cfg.maxAbsZ};
526 
527  // (startPoint, direction), distance
528  std::vector<
529  std::pair<std::pair<Acts::Vector3, Acts::Vector3>, Acts::ActsScalar>>
530  tripletsWithRays;
531  tripletsWithRays.reserve(triplets.size());
532 
533  for (const auto& triplet : triplets) {
534  tripletsWithRays.emplace_back(
535  std::make_pair(triplet.ray.origin(), triplet.ray.dir()), -1.);
536  }
537 
538  // elements of the linear equations to solve
540  Acts::SquareMatrix3::Identity() * 2. * triplets.size();
541  Acts::Vector3 B = Acts::Vector3::Zero();
542  for (const auto& triplet : tripletsWithRays) {
543  // use ray saved from earlier
544  const auto& startPoint = triplet.first.first;
545  const auto& direction = triplet.first.second;
546 
547  A -= 2. * (direction * direction.transpose());
548  B += -2. * direction * (direction.dot(startPoint)) + 2. * startPoint;
549  }
550 
551  for (std::uint32_t iter = 0; iter <= m_cfg.maxIterations; iter++) {
552  // new vertex position
553  vtx = A.lu().solve(B);
554 
555  Acts::Vector3 vtxDiff = vtx - vtxPrev;
556 
557  if (vtxDiff.norm() < m_cfg.minVtxShift) {
558  // difference between the new vertex and the old vertex is not so large
559  break;
560  }
561 
562  if (iter != m_cfg.maxIterations) {
563  // is not the last iteration
564  vtxPrev = vtx;
565 
566  for (auto& triplet : tripletsWithRays) {
567  const auto& startPoint = triplet.first.first;
568  const auto& direction = triplet.first.second;
569  Acts::ActsScalar distance = (vtx - startPoint).cross(direction).norm();
570  triplet.second = distance;
571  }
572 
573  std::sort(tripletsWithRays.begin(), tripletsWithRays.end(),
574  [](const auto& lhs, const auto& rhs) {
575  return lhs.second < rhs.second;
576  });
577 
578  std::uint32_t threshold = (std::uint32_t)(tripletsWithRays.size() *
579  (1. - m_cfg.removeFraction));
580 
581  for (std::uint32_t tr = threshold + 1; tr < tripletsWithRays.size();
582  ++tr) {
583  const auto& startPoint = tripletsWithRays[tr].first.first;
584  const auto& direction = tripletsWithRays[tr].first.second;
585 
586  // remove this triplet from A and B
587  A -= Acts::SquareMatrix3::Identity() * 2.;
588  A += 2. * (direction * direction.transpose());
589  B -= -2. * direction * (direction.dot(startPoint)) + 2. * startPoint;
590  }
591 
592  // remove all excessive triplets
593  tripletsWithRays.resize(threshold);
594  }
595  }
596 
597  return vtx;
598 }