Warning, file /acts/Core/src/Seeding/GbtsGraphBuilder.cpp was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Seeding/GbtsGraphBuilder.hpp"
0010
0011 #include "Acts/SpacePointFormation/detail/StripSpacePointCalibrationImpl.hpp"
0012 #include "Acts/Utilities/MathHelpers.hpp"
0013
0014 #include <algorithm>
0015 #include <array>
0016 #include <cmath>
0017 #include <cstdint>
0018 #include <numbers>
0019 #include <span>
0020 #include <stdexcept>
0021 #include <utility>
0022 #include <vector>
0023
0024 namespace Acts::Experimental {
0025
0026 namespace {
0027
0028
0029
0030
0031
0032 struct SlidingWindow {
0033
0034 const std::pair<float, SpacePointIndex>* phiNodes{};
0035
0036 std::uint32_t numPhiNodes{};
0037
0038 std::uint32_t firstIt{};
0039
0040 float deltaPhi{};
0041
0042 std::int32_t barrelOrder{-1};
0043
0044 GbtsLayerType type{};
0045
0046 GbtsLayerTechnology technology{};
0047 };
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058 bool validateTriplet(const GbtsGraphBuilder::Config& cfg,
0059 const detail::GbtsNodeView& nodeView,
0060 const std::array<SpacePointIndex, 3>& candidateTriplet,
0061 const float tripletMinPt, const float tauRatio,
0062 const float tauRatioCut, const float bFieldInZ) {
0063
0064
0065 std::array<float, 2> u{};
0066 std::array<float, 2> v{};
0067
0068 const detail::GbtsNodeProxy n0 = nodeView[candidateTriplet[1]];
0069
0070 const float x0 = n0.x();
0071 const float y0 = n0.y();
0072
0073 const float r0 = n0.r();
0074
0075 const float cosA = x0 / r0;
0076
0077 const float sinA = y0 / r0;
0078
0079 for (std::uint32_t k = 0; k < 2; k++) {
0080 const std::uint32_t spIdx = (k == 1) ? 2 : k;
0081
0082 const detail::GbtsNodeProxy nk = nodeView[candidateTriplet[spIdx]];
0083
0084 const float dx = nk.x() - x0;
0085
0086 const float dy = nk.y() - y0;
0087
0088 const float r2Inv = 1.0f / (dx * dx + dy * dy);
0089
0090 const float xn = dx * cosA + dy * sinA;
0091
0092 const float yn = -dx * sinA + dy * cosA;
0093
0094 u[k] = xn * r2Inv;
0095 v[k] = yn * r2Inv;
0096 }
0097
0098 const float du = u[0] - u[1];
0099
0100 if (du == 0.0) {
0101 return false;
0102 }
0103
0104 const float A = (v[0] - v[1]) / du;
0105
0106 const float B = v[1] - A * u[1];
0107
0108 const float d0 = r0 * (B * r0 - A);
0109
0110 if (std::abs(d0) > cfg.d0Max) {
0111 return false;
0112 }
0113
0114 if (B != 0.0) {
0115
0116 const float R = std::sqrt(1 + A * A) / B;
0117
0118
0119 const float pT = std::abs(bFieldInZ * R / 2);
0120
0121 if (pT < tripletMinPt) {
0122 return false;
0123 }
0124
0125 if (pT > 5 * tripletMinPt) {
0126
0127 if (tauRatio > 0.9f * tauRatioCut) {
0128 return false;
0129 }
0130 }
0131 }
0132
0133 return true;
0134 }
0135
0136
0137
0138
0139
0140
0141
0142
0143 bool checkZ0BitMask(const GbtsGraphBuilder::Config& cfg,
0144 const std::uint16_t z0BitMask, const float z0,
0145 const float z0HistoCoeff) {
0146 if (z0BitMask == 0) {
0147 return true;
0148 }
0149
0150
0151
0152 const auto isSet = [z0BitMask](const std::int32_t bin) {
0153 return bin >= 0 && bin < detail::kGbtsZ0HistogramBins &&
0154 ((z0BitMask >> bin) & 1) != 0;
0155 };
0156
0157 const float dz = z0 - cfg.minZ0;
0158 const std::int32_t z0BinIndex = static_cast<std::int32_t>(z0HistoCoeff * dz);
0159
0160 if (isSet(z0BinIndex)) {
0161 return true;
0162 }
0163
0164
0165
0166 const float dzm = dz - cfg.z0Resolution;
0167
0168 std::int32_t nextBin = static_cast<std::int32_t>(z0HistoCoeff * dzm);
0169
0170 if (nextBin != z0BinIndex && isSet(nextBin)) {
0171 return true;
0172 }
0173
0174 const float dzp = dz + cfg.z0Resolution;
0175
0176 nextBin = static_cast<std::int32_t>(z0HistoCoeff * dzp);
0177
0178 if (nextBin != z0BinIndex && isSet(nextBin)) {
0179 return true;
0180 }
0181
0182 return false;
0183 }
0184
0185 }
0186
0187 GbtsGraphBuilder::GbtsGraphBuilder(const Config& config,
0188 std::shared_ptr<const GbtsGeometry> geometry,
0189 std::unique_ptr<const Acts::Logger> logger)
0190 : m_cfg(config),
0191 m_geometry(std::move(geometry)),
0192 m_logger(std::move(logger)) {
0193
0194
0195 if (m_cfg.tauRatioCorr < 0) {
0196 throw std::invalid_argument(
0197 "GbtsGraphBuilder: tauRatioCorr must not be negative");
0198 }
0199
0200 if (m_cfg.tauRatioCorrStrip < 0) {
0201 throw std::invalid_argument(
0202 "GbtsGraphBuilder: tauRatioCorrStrip must not be negative");
0203 }
0204 }
0205
0206 GbtsGraph GbtsGraphBuilder::buildTheGraph(const GbtsRoiDescriptor& roi,
0207 GbtsNodeStorage& nodeStorage,
0208 const float bFieldInZ) const {
0209 GbtsGraph graph;
0210 std::vector<detail::GbtsEdge>& edgeStorage = graph.edgeStorage;
0211
0212
0213 const float cutZMinU =
0214 m_cfg.minZ0 + m_cfg.maxOuterRadius * static_cast<float>(roi.dzdrMin());
0215 const float cutZMaxU =
0216 m_cfg.maxZ0 + m_cfg.maxOuterRadius * static_cast<float>(roi.dzdrMax());
0217
0218 const float tripletPtMin = m_cfg.tripletPtFraction * m_cfg.minPt;
0219
0220 const float ptScale = m_cfg.tuningPt / m_cfg.minPt;
0221
0222 const float curvatureCutHighEta = m_cfg.maxCurvatureHighEta * ptScale;
0223 const float curvatureCutLowEta = m_cfg.maxCurvatureLowEta * ptScale;
0224
0225
0226 const float maxTauRatioCut =
0227 m_cfg.tauRatioCut + (m_cfg.useAdaptiveCuts ? m_cfg.tauRatioCorr : 0.0f) +
0228 (nodeStorage.hasStrips() ? m_cfg.tauRatioCorrStrip : 0.0f);
0229
0230
0231
0232 const float deltaPhi0 = 0.5f * nodeStorage.config().phiSliceWidth;
0233
0234 std::uint32_t nConnections = 0;
0235
0236
0237
0238 const auto maxEdges = static_cast<std::size_t>(
0239 m_cfg.maxEdgesPerSP * static_cast<float>(nodeStorage.numberOfNodes()));
0240
0241 edgeStorage.reserve(maxEdges);
0242
0243
0244 const float z0HistoCoeff =
0245 detail::kGbtsZ0HistogramBins / (m_cfg.maxZ0 - m_cfg.minZ0 + 1e-6f);
0246
0247 const detail::GbtsNodeView nodeView = nodeStorage.nodeView();
0248 const std::span<const detail::GbtsNodeParams> params =
0249 nodeStorage.nodeParams();
0250 const std::span<detail::GbtsNodeEdgeInfo> edgeInfo =
0251 nodeStorage.nodeEdgeInfo();
0252
0253
0254 std::vector<SlidingWindow> phiSlidingWindow;
0255
0256 const bool calibrate = m_cfg.calibrateStrips && nodeStorage.hasStrips();
0257
0258
0259
0260
0261 const auto calibrateNode = [&](const SpacePointIndex node,
0262 const std::array<float, 3>& direction,
0263 float& r, float& z) {
0264 std::array<float, 3> point{};
0265 if (!Acts::detail::calibrateOuterStripSpacePoint(
0266 direction, nodeStorage.strip(node), point,
0267 m_cfg.maxStripLengthFraction)) {
0268 return false;
0269 }
0270 r = fastHypot(point[0], point[1]);
0271 z = point[2];
0272 return true;
0273 };
0274
0275
0276 for (const auto& bg : m_geometry->binGroups()) {
0277 const detail::GbtsEtaBinInfo& B1 = nodeStorage.etaBin(bg.bin);
0278
0279 if (B1.empty()) {
0280 continue;
0281 }
0282
0283 const float rb1 = B1.minRadius;
0284
0285 const std::int32_t barrelOrder1 = B1.barrelOrder;
0286
0287 const bool isPixel1 = B1.technology == GbtsLayerTechnology::Pixel;
0288 const bool isPixelBarrel1 = barrelOrder1 >= 0;
0289
0290 const bool useZ0Histogram =
0291 barrelOrder1 >= 0 && barrelOrder1 <= m_cfg.z0HistogramMaxBarrelOrder;
0292 const bool useMatchBeforeCreate =
0293 m_cfg.matchBeforeCreate && barrelOrder1 >= 0 &&
0294 barrelOrder1 <= m_cfg.matchBeforeCreateMaxBarrelOrder;
0295
0296
0297
0298 phiSlidingWindow.clear();
0299
0300
0301 for (const std::uint32_t b2Idx : bg.links) {
0302 const detail::GbtsEtaBinInfo& B2 = nodeStorage.etaBin(b2Idx);
0303
0304 if (B2.empty()) {
0305 continue;
0306 }
0307
0308 const float rb2 = B2.maxRadius;
0309
0310 float deltaPhi = deltaPhi0;
0311
0312
0313 if (m_cfg.useEtaBinning) {
0314 const float absDr = std::fabs(rb2 - rb1);
0315 if (absDr < m_cfg.phiWindowSplitDeltaRadius) {
0316 deltaPhi = m_cfg.phiWindowNearOffset +
0317 m_cfg.phiWindowNearSlope * ptScale * absDr;
0318 } else {
0319 deltaPhi = m_cfg.phiWindowFarOffset +
0320 m_cfg.phiWindowFarSlope * ptScale * absDr;
0321 }
0322 }
0323
0324 SlidingWindow& window = phiSlidingWindow.emplace_back();
0325 window.phiNodes = B2.phiNodes.data();
0326 window.numPhiNodes = static_cast<std::uint32_t>(B2.phiNodes.size());
0327 window.deltaPhi = deltaPhi;
0328 window.barrelOrder = B2.barrelOrder;
0329 window.type = B2.type;
0330 window.technology = B2.technology;
0331 }
0332
0333
0334 for (SpacePointIndex n1Idx = B1.nodes.first; n1Idx < B1.nodes.second;
0335 ++n1Idx) {
0336
0337 edgeInfo[n1Idx].firstEdge =
0338 static_cast<std::uint32_t>(edgeStorage.size());
0339
0340
0341 std::uint16_t numCreatedEdges = 0;
0342
0343 bool isConnected = false;
0344
0345 std::array<std::uint8_t, detail::kGbtsZ0HistogramBins> z0Histo = {};
0346
0347 const detail::GbtsNodeParams& n1pars = params[n1Idx];
0348
0349 const float phi1 = n1pars.phi;
0350 const float r1 = n1pars.r;
0351 const float z1 = n1pars.z;
0352
0353
0354 float x1 = 0.f;
0355 float y1 = 0.f;
0356 if (calibrate) {
0357 const std::array<float, 4>& position = nodeView.positions[n1Idx];
0358 x1 = position[0];
0359 y1 = position[1];
0360 }
0361
0362
0363 for (auto& slw : phiSlidingWindow) {
0364 const std::int32_t barrelOrder2 = slw.barrelOrder;
0365
0366 const bool isPixel2 = slw.technology == GbtsLayerTechnology::Pixel;
0367 const bool isPixelBarrel2 = barrelOrder2 >= 0;
0368
0369 const bool stripPair = calibrate && (!isPixel1 || !isPixel2);
0370
0371 const float deltaPhi = slw.deltaPhi;
0372
0373
0374
0375 const float minPhi = phi1 - deltaPhi;
0376 const float maxPhi = phi1 + deltaPhi;
0377
0378
0379 for (std::uint32_t n2PhiIdx = slw.firstIt; n2PhiIdx < slw.numPhiNodes;
0380 ++n2PhiIdx) {
0381 const float phi2 = slw.phiNodes[n2PhiIdx].first;
0382
0383 if (phi2 < minPhi) {
0384
0385 slw.firstIt = n2PhiIdx;
0386 continue;
0387 }
0388 if (phi2 > maxPhi) {
0389
0390 break;
0391 }
0392
0393 const SpacePointIndex n2Idx = slw.phiNodes[n2PhiIdx].second;
0394
0395 const detail::GbtsNodeEdgeInfo& n2Info = edgeInfo[n2Idx];
0396
0397 const std::uint16_t nodeInfo = n2Info.isConnected;
0398
0399
0400 if (useZ0Histogram && (nodeInfo == 0)) {
0401 continue;
0402 }
0403
0404 const std::uint32_t n2FirstEdge = n2Info.firstEdge;
0405 const std::uint16_t n2NumEdges = n2Info.numEdges;
0406 const std::uint32_t n2LastEdge = n2FirstEdge + n2NumEdges;
0407
0408 const detail::GbtsNodeParams& n2pars = params[n2Idx];
0409
0410 const float r2 = n2pars.r;
0411
0412 float dr = r2 - r1;
0413
0414
0415
0416 if (dr < m_cfg.minDeltaRadius) {
0417 continue;
0418 }
0419
0420 const float z2 = n2pars.z;
0421
0422
0423
0424
0425
0426 float r1c = r1;
0427 float z1c = z1;
0428 float r2c = r2;
0429 float z2c = z2;
0430
0431 if (stripPair) {
0432
0433
0434 const std::array<float, 4>& position = nodeView.positions[n2Idx];
0435 const float dx = position[0] - x1;
0436 const float dy = position[1] - y1;
0437 const float dzc = z2 - z1;
0438 const float turn = phi2 - phi1;
0439
0440 bool resolved = true;
0441 if (!isPixel1) {
0442 resolved = calibrateNode(
0443 n1Idx, {dx + dy * turn, dy - dx * turn, dzc}, r1c, z1c);
0444 }
0445 if (resolved && !isPixel2) {
0446 resolved = calibrateNode(
0447 n2Idx, {dx - dy * turn, dy + dx * turn, dzc}, r2c, z2c);
0448 }
0449 if (!resolved) {
0450 continue;
0451 }
0452
0453 dr = r2c - r1c;
0454 if (dr < m_cfg.minDeltaRadius) {
0455 continue;
0456 }
0457 }
0458
0459 const float dz = z2c - z1c;
0460 const float tau = dz / dr;
0461 const float ftau = std::fabs(tau);
0462 if (ftau > m_cfg.maxAbsTau) {
0463 continue;
0464 }
0465
0466 if (ftau < n1pars.minTau) {
0467 continue;
0468 }
0469 if (ftau > n1pars.maxTau) {
0470 continue;
0471 }
0472
0473 if (ftau < n2pars.minTau) {
0474 continue;
0475 }
0476 if (ftau > n2pars.maxTau) {
0477 continue;
0478 }
0479
0480 const float z0 = z1c - r1c * tau;
0481
0482
0483 if (useZ0Histogram &&
0484 !checkZ0BitMask(m_cfg, nodeInfo, z0, z0HistoCoeff)) {
0485 continue;
0486 }
0487
0488 if (m_cfg.doubletFilterRZ) {
0489 if (z0 < m_cfg.minZ0 || z0 > m_cfg.maxZ0) {
0490 continue;
0491 }
0492
0493 const float zOuter = z0 + m_cfg.maxOuterRadius * tau;
0494
0495 if (zOuter < cutZMinU || zOuter > cutZMaxU) {
0496 continue;
0497 }
0498 }
0499
0500 const float curv = (phi2 - phi1) / dr;
0501
0502 if (std::abs(curv) > (ftau < m_cfg.curvatureSplitAbsTau
0503 ? curvatureCutLowEta
0504 : curvatureCutHighEta)) {
0505 continue;
0506 }
0507
0508 const float hypotTau = fastHypot(1, tau);
0509 const float expEta = hypotTau - tau;
0510
0511
0512 const float invExpEta = hypotTau + tau;
0513
0514
0515 if (useMatchBeforeCreate) {
0516
0517 bool isGood = n2NumEdges <= m_cfg.matchBeforeCreateMaxEdges;
0518
0519 if (!isGood) {
0520 const float uat1 = invExpEta;
0521
0522 for (std::uint32_t n2InIdx = n2FirstEdge; n2InIdx < n2LastEdge;
0523 ++n2InIdx) {
0524 const float tau2 = edgeStorage[n2InIdx].p[0];
0525 const float tauRatio = tau2 * uat1 - 1.0f;
0526
0527 if (std::abs(tauRatio) > m_cfg.tauRatioPrecut) {
0528 continue;
0529 }
0530 isGood = true;
0531 break;
0532 }
0533 }
0534
0535 if (!isGood) {
0536 continue;
0537 }
0538 }
0539
0540 const float dPhi2 = curv * r2c;
0541 const float dPhi1 = curv * r1c;
0542
0543 if (edgeStorage.size() < maxEdges) {
0544 const auto outEdgeIdx =
0545 static_cast<std::uint32_t>(edgeStorage.size());
0546
0547 edgeStorage.emplace_back(n1Idx, n2Idx, barrelOrder2, expEta, curv,
0548 phi1 + dPhi1);
0549
0550 ++numCreatedEdges;
0551
0552 const float uat2 = invExpEta;
0553 const float phi2u = phi2 + dPhi2;
0554 const float curv2 = curv;
0555
0556
0557 for (std::uint32_t inEdgeIdx = n2FirstEdge; inEdgeIdx < n2LastEdge;
0558 ++inEdgeIdx) {
0559 detail::GbtsEdge* pS = &edgeStorage[inEdgeIdx];
0560
0561 const float absTauRatio = std::abs(pS->p[0] * uat2 - 1.0f);
0562
0563
0564 if (absTauRatio > maxTauRatioCut) {
0565 continue;
0566 }
0567
0568 if (pS->nNei >= detail::kGbtsMaxEdgeNeighbours) {
0569 continue;
0570 }
0571
0572 const std::int32_t barrelOrder3 = pS->n2BarrelOrder;
0573
0574 const bool isPixelBarrel3 = barrelOrder3 >= 0;
0575
0576 float addTauRatioCorr = 0;
0577
0578 if (m_cfg.useAdaptiveCuts) {
0579 if (isPixelBarrel1 && isPixelBarrel2 && isPixelBarrel3) {
0580
0581 const bool noGap = (barrelOrder2 - barrelOrder1) == 1 &&
0582 (barrelOrder3 - barrelOrder2) == 1;
0583
0584
0585 if (!noGap) {
0586 addTauRatioCorr = m_cfg.tauRatioCorr;
0587 }
0588 } else {
0589 bool mixedTriplet =
0590 isPixelBarrel1 && isPixelBarrel2 && !isPixelBarrel3;
0591 if (mixedTriplet) {
0592 addTauRatioCorr = m_cfg.tauRatioCorr;
0593 }
0594 }
0595 }
0596
0597
0598
0599 if (m_cfg.tauRatioCorrStrip > 0.f &&
0600 (!isPixel1 || !isPixel2 ||
0601 nodeView.strip(pS->n2) != nullptr)) {
0602 addTauRatioCorr += m_cfg.tauRatioCorrStrip;
0603 }
0604
0605
0606 if (absTauRatio > m_cfg.tauRatioCut + addTauRatioCorr) {
0607 continue;
0608 }
0609
0610 float dPhi = phi2u - pS->p[2];
0611
0612 if (dPhi < -std::numbers::pi_v<float>) {
0613 dPhi += 2 * std::numbers::pi_v<float>;
0614 } else if (dPhi > std::numbers::pi_v<float>) {
0615 dPhi -= 2 * std::numbers::pi_v<float>;
0616 }
0617
0618 if (std::abs(dPhi) > m_cfg.cutDPhiMax) {
0619 continue;
0620 }
0621
0622 const float dcurv = curv2 - pS->p[1];
0623
0624 if (dcurv < -m_cfg.cutDCurvMax || dcurv > m_cfg.cutDCurvMax) {
0625 continue;
0626 }
0627
0628
0629 if (m_cfg.validateTriplets && isPixelBarrel1 && isPixelBarrel2 &&
0630 isPixelBarrel3) {
0631 const std::array<SpacePointIndex, 3> candidateTriplet = {
0632 n1Idx, n2Idx, pS->n2};
0633
0634 if (!validateTriplet(m_cfg, nodeView, candidateTriplet,
0635 tripletPtMin, absTauRatio,
0636 m_cfg.tauRatioCut, bFieldInZ)) {
0637 continue;
0638 }
0639 }
0640
0641 pS->vNei[pS->nNei] = outEdgeIdx;
0642 ++pS->nNei;
0643
0644 isConnected = true;
0645
0646
0647
0648 if (const auto z0BinIndex = static_cast<std::int32_t>(
0649 z0HistoCoeff * (z0 - m_cfg.minZ0));
0650 z0BinIndex >= 0 &&
0651 z0BinIndex < detail::kGbtsZ0HistogramBins) {
0652 ++z0Histo[static_cast<std::size_t>(z0BinIndex)];
0653 }
0654
0655 nConnections++;
0656 }
0657 }
0658 }
0659 }
0660
0661
0662
0663 edgeInfo[n1Idx].numEdges = numCreatedEdges;
0664 if (isConnected) {
0665 std::uint16_t z0BitMask = 0x0;
0666
0667 for (std::int32_t bIdx = 0; bIdx < detail::kGbtsZ0HistogramBins;
0668 ++bIdx) {
0669 if (z0Histo[static_cast<std::size_t>(bIdx)] == 0) {
0670 continue;
0671 }
0672
0673 z0BitMask |= (1 << bIdx);
0674 }
0675
0676
0677 edgeInfo[n1Idx].isConnected = z0BitMask;
0678 }
0679
0680 }
0681 }
0682
0683 if (edgeStorage.size() >= maxEdges) {
0684 ACTS_WARNING(
0685 "Maximum number of graph edges exceeded - possible efficiency loss "
0686 << edgeStorage.size());
0687 }
0688 graph.nConnections = nConnections;
0689 return graph;
0690 }
0691
0692 std::uint32_t GbtsGraph::runCCA(const std::uint32_t maxIterations) {
0693 const auto nEdges = static_cast<std::uint32_t>(edgeStorage.size());
0694
0695 std::uint32_t maxLevel = 0;
0696
0697 std::uint32_t iter = 0;
0698
0699 std::vector<detail::GbtsEdge*> vOld;
0700
0701 for (std::uint32_t edgeIndex = 0; edgeIndex < nEdges; ++edgeIndex) {
0702 detail::GbtsEdge* pS = &(edgeStorage[edgeIndex]);
0703 if (pS->nNei == 0) {
0704 continue;
0705 }
0706
0707
0708
0709 vOld.push_back(pS);
0710 }
0711
0712 std::vector<detail::GbtsEdge*> vNew;
0713 vNew.reserve(vOld.size());
0714
0715
0716 for (; iter < maxIterations; iter++) {
0717 vNew.clear();
0718
0719 for (detail::GbtsEdge* pS : vOld) {
0720 std::int32_t nextLevel = pS->level;
0721
0722 for (std::uint32_t nIdx = 0; nIdx < pS->nNei; ++nIdx) {
0723 const std::uint32_t nextEdgeIdx = pS->vNei[nIdx];
0724
0725 const detail::GbtsEdge* pN = &(edgeStorage[nextEdgeIdx]);
0726
0727 if (pS->level == pN->level) {
0728 nextLevel = pS->level + 1;
0729 vNew.push_back(pS);
0730 break;
0731 }
0732 }
0733
0734
0735 pS->next = static_cast<std::int8_t>(nextLevel);
0736 }
0737
0738
0739
0740 std::uint32_t nChanges = 0;
0741
0742 for (auto pS : vNew) {
0743 if (pS->next != pS->level) {
0744 nChanges++;
0745 pS->level = pS->next;
0746
0747 maxLevel = std::max(maxLevel, static_cast<std::uint32_t>(pS->level));
0748 }
0749 }
0750
0751 if (nChanges == 0) {
0752 break;
0753 }
0754
0755 vOld.swap(vNew);
0756 vNew.clear();
0757 }
0758
0759 return maxLevel;
0760 }
0761
0762 std::vector<detail::GbtsEdge*> GbtsGraph::extractChainHeads(
0763 const std::uint8_t minSeedLevel, const bool addTriplets,
0764 const float maxAbsEtaAddTriplets) {
0765 const auto nEdges = static_cast<std::uint32_t>(edgeStorage.size());
0766
0767
0768
0769 const std::int8_t minLevelAddTriplets =
0770 static_cast<std::int8_t>(minSeedLevel - 1);
0771 std::vector<detail::GbtsEdge*> vChainHeads;
0772
0773 vChainHeads.reserve(nEdges / 2);
0774
0775 for (std::uint32_t edgeIndex = 0; edgeIndex < nEdges; ++edgeIndex) {
0776 detail::GbtsEdge* pS = &edgeStorage[edgeIndex];
0777
0778 if (!addTriplets) {
0779 if (pS->level < minSeedLevel) {
0780 continue;
0781 }
0782 } else {
0783 const float edgeAbsEta = std::abs(-std::log(pS->p[0]));
0784
0785 if (edgeAbsEta > maxAbsEtaAddTriplets) {
0786 if (pS->level < minSeedLevel) {
0787 continue;
0788 }
0789 } else {
0790 if (pS->level < minLevelAddTriplets) {
0791 continue;
0792 }
0793 }
0794 }
0795
0796 vChainHeads.push_back(pS);
0797 }
0798
0799 std::ranges::sort(vChainHeads, std::ranges::greater{},
0800 [](const detail::GbtsEdge* e) { return e->level; });
0801
0802 return vChainHeads;
0803 }
0804
0805 }