File indexing completed on 2026-07-26 08:18:59
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Seeding/GraphBasedTrackSeeder.hpp"
0010
0011 #include "Acts/Seeding/GbtsTrackingFilter.hpp"
0012 #include "Acts/Utilities/MathHelpers.hpp"
0013
0014 #include <algorithm>
0015 #include <cmath>
0016 #include <cstdint>
0017 #include <fstream>
0018 #include <memory>
0019 #include <numbers>
0020 #include <utility>
0021 #include <vector>
0022
0023 namespace Acts::Experimental {
0024
0025 GraphBasedTrackSeeder::DerivedConfig::DerivedConfig(const Config& config)
0026 : Config(config) {
0027 phiSliceWidth = 2 * std::numbers::pi_v<float> / config.nMaxPhiSlice;
0028 }
0029
0030 GraphBasedTrackSeeder::Options::Options(float bFieldInZ_)
0031 : bFieldInZ(bFieldInZ_) {
0032 ptCoeff = 0.5f * bFieldInZ * Acts::UnitConstants::m;
0033 }
0034
0035 GraphBasedTrackSeeder::GraphBasedTrackSeeder(
0036 const DerivedConfig& config, std::shared_ptr<GbtsGeometry> geometry,
0037 std::unique_ptr<const Acts::Logger> logger)
0038 : m_cfg(config),
0039 m_geometry(std::move(geometry)),
0040 m_logger(std::move(logger)) {
0041 m_mlLut = parseGbtsMlLookupTable(m_cfg.lutInputFile);
0042 }
0043
0044 void GraphBasedTrackSeeder::createSeeds(const SpacePointContainer& spacePoints,
0045 const GbtsRoiDescriptor& roi,
0046 const std::vector<bool>& isPixelLayer,
0047 const std::uint32_t maxLayers,
0048 const GbtsTrackingFilter& filter,
0049 const Options& options,
0050 SeedContainer& outputSeeds) const {
0051 const std::vector<std::vector<GbtsNode>> nodesPerLayer =
0052 createNodes(spacePoints, maxLayers);
0053
0054 createSeeds(nodesPerLayer, isPixelLayer, roi, filter, options, outputSeeds);
0055 }
0056
0057 void GraphBasedTrackSeeder::createSeeds(
0058 const std::vector<std::vector<GbtsNode>>& nodesPerLayer,
0059 const std::vector<bool>& isPixelLayer, const GbtsRoiDescriptor& roi,
0060 const GbtsTrackingFilter& filter, const Options& options,
0061 SeedContainer& outputSeeds) const {
0062 GbtsNodeStorage nodeStorage(m_geometry, m_mlLut);
0063
0064 std::uint32_t nPixelLoaded = 0;
0065 std::uint32_t nStripLoaded = 0;
0066
0067 std::uint32_t nHits = 0;
0068
0069 for (std::uint16_t l = 0; l < nodesPerLayer.size(); l++) {
0070 const std::vector<GbtsNode>& nodes = nodesPerLayer[l];
0071 nHits += nodes.size();
0072
0073 if (nodes.empty()) {
0074 continue;
0075 }
0076
0077
0078 const bool isPixel = isPixelLayer[l];
0079
0080 if (isPixel) {
0081 nPixelLoaded += nodeStorage.loadPixelGraphNodes(
0082 l, nodes, m_cfg.useMl, m_cfg.maxEndcapClusterWidth);
0083 } else {
0084 nStripLoaded += nodeStorage.loadStripGraphNodes(l, nodes);
0085 }
0086 }
0087 ACTS_DEBUG("Loaded " << nPixelLoaded << " pixel space points and "
0088 << nStripLoaded << " strip space points");
0089
0090 nodeStorage.sortByPhi();
0091
0092 nodeStorage.initializeNodes(m_cfg.useMl);
0093
0094 nodeStorage.generatePhiIndexing(1.5f * m_cfg.phiSliceWidth);
0095
0096 std::vector<GbtsEdge> edgeStorage;
0097
0098 std::pair<std::int32_t, std::int32_t> graphStats =
0099 buildTheGraph(roi, nodeStorage, edgeStorage, options);
0100
0101 ACTS_DEBUG("Created graph with " << graphStats.first << " edges and "
0102 << graphStats.second << " edge links");
0103
0104 if (graphStats.first == 0 || graphStats.second == 0) {
0105 ACTS_WARNING("Missing edges or edge connections");
0106 }
0107
0108 std::uint32_t maxLevel = runCCA(graphStats.first, edgeStorage);
0109
0110 ACTS_DEBUG("Reached Level " << maxLevel << " after GNN iterations");
0111
0112 std::vector<OutputSeedProperties> vOutputSeeds;
0113 extractSeedsFromTheGraph(maxLevel, graphStats.first, nHits, edgeStorage,
0114 vOutputSeeds, filter);
0115
0116 ACTS_DEBUG("GBTS created " << vOutputSeeds.size() << " seeds");
0117 if (vOutputSeeds.empty()) {
0118 ACTS_WARNING("No Seed Candidates");
0119 }
0120
0121
0122 for (const auto& seed : vOutputSeeds) {
0123 auto newSeed = outputSeeds.createSeed();
0124 newSeed.assignSpacePointIndices(seed.spacePoints);
0125 newSeed.quality() = seed.seedQuality;
0126 }
0127 }
0128
0129 GbtsMlLookupTable GraphBasedTrackSeeder::parseGbtsMlLookupTable(
0130 const std::string& lutInputFile) {
0131 if (!m_cfg.useMl) {
0132 return {};
0133 }
0134 if (lutInputFile.empty()) {
0135 throw std::runtime_error("Cannot find ML predictor LUT file");
0136 }
0137
0138 std::ifstream ifs(std::string(lutInputFile).c_str());
0139 if (!ifs.is_open()) {
0140 throw std::runtime_error("Failed to open LUT file");
0141 }
0142
0143 GbtsMlLookupTable mlLut;
0144 mlLut.reserve(100);
0145
0146 float clWidth{};
0147 float min1{};
0148 float max1{};
0149 float min2{};
0150 float max2{};
0151 while (ifs >> clWidth >> min1 >> max1 >> min2 >> max2) {
0152 mlLut.emplace_back(std::array<float, 5>{clWidth, min1, max1, min2, max2});
0153 }
0154
0155 if (!ifs.eof()) {
0156
0157 throw std::runtime_error("Stopped reading LUT file due to parse error");
0158 }
0159
0160 return mlLut;
0161 }
0162
0163 std::vector<std::vector<GbtsNode>> GraphBasedTrackSeeder::createNodes(
0164 const SpacePointContainer& spacePoints,
0165 const std::uint32_t maxLayers) const {
0166 auto layerColumn = spacePoints.column<std::uint32_t>("layerId");
0167 auto clusterWidthColumn = spacePoints.column<float>("clusterWidth");
0168 auto localPositionColumn = spacePoints.column<float>("localPositionY");
0169
0170 std::vector<std::vector<GbtsNode>> nodesPerLayer(maxLayers);
0171
0172 for (auto& v : nodesPerLayer) {
0173 v.reserve(10000);
0174 }
0175
0176
0177 std::vector<bool> pixelLayers{};
0178 pixelLayers.reserve(maxLayers);
0179
0180 for (const auto& sp : spacePoints) {
0181
0182
0183 const std::uint16_t layer = sp.extra(layerColumn);
0184
0185
0186 GbtsNode& node = nodesPerLayer[layer].emplace_back(layer);
0187
0188
0189
0190 node.x = sp.x();
0191 node.y = sp.y();
0192 node.z = sp.z();
0193 node.r = sp.r();
0194 node.phi = sp.phi();
0195 node.idx = sp.index();
0196 node.pcw = sp.extra(clusterWidthColumn);
0197 node.locPosY = sp.extra(localPositionColumn);
0198 }
0199
0200 return nodesPerLayer;
0201 }
0202
0203 std::pair<std::int32_t, std::int32_t> GraphBasedTrackSeeder::buildTheGraph(
0204 const GbtsRoiDescriptor& roi, GbtsNodeStorage& nodeStorage,
0205 std::vector<GbtsEdge>& edgeStorage, const Options& options) const {
0206
0207 const float cutZMinU =
0208 m_cfg.minZ0 + m_cfg.maxOuterRadius * static_cast<float>(roi.dzdrMin());
0209 const float cutZMaxU =
0210 m_cfg.maxZ0 + m_cfg.maxOuterRadius * static_cast<float>(roi.dzdrMax());
0211
0212
0213 const float tripletPtMin = 0.8f * m_cfg.minPt;
0214
0215
0216 const float ptScale = 0.9f / m_cfg.minPt;
0217
0218 const float maxCurv = options.ptCoeff / tripletPtMin;
0219
0220 float maxKappaHighEta =
0221 m_cfg.lrtMode ? 1.0f * maxCurv : std::sqrt(0.8f) * maxCurv;
0222 float maxKappaLowEta =
0223 m_cfg.lrtMode ? 1.0f * maxCurv : std::sqrt(0.6f) * maxCurv;
0224
0225
0226 if (!m_cfg.useOldTunings && !m_cfg.lrtMode) {
0227 maxKappaHighEta = 4.75e-4f * ptScale;
0228 maxKappaLowEta = 3.75e-4f * ptScale;
0229 }
0230
0231 const float dPhiCoeff = m_cfg.lrtMode ? 1.0f * maxCurv : 0.68f * maxCurv;
0232
0233
0234 const float deltaPhi0 = 0.5f * m_cfg.phiSliceWidth;
0235
0236 std::uint32_t nConnections = 0;
0237
0238 edgeStorage.reserve(m_cfg.nMaxEdges);
0239
0240 std::uint32_t nEdges = 0;
0241
0242
0243
0244
0245 const std::uint32_t zBins = 16;
0246 const float z0HistoCoeff = zBins / (m_cfg.maxZ0 - m_cfg.minZ0 + 1e-6);
0247
0248
0249 for (const auto& bg : m_geometry->binGroups()) {
0250 GbtsEtaBin& B1 = nodeStorage.getEtaBin(bg.first);
0251
0252 if (B1.empty()) {
0253 continue;
0254 }
0255
0256 const float rb1 = B1.minRadius;
0257
0258 const std::uint32_t layerId1 = B1.layerId;
0259
0260 const bool isBarrel1 = (layerId1 / 10000) == 8;
0261
0262
0263
0264 std::vector<SlidingWindow> phiSlidingWindow;
0265
0266
0267 phiSlidingWindow.resize(bg.second.size());
0268
0269 std::uint32_t winIdx = 0;
0270
0271
0272 for (const auto& b2Idx : bg.second) {
0273 const GbtsEtaBin& B2 = nodeStorage.getEtaBin(b2Idx);
0274
0275 if (B2.empty()) {
0276 ++winIdx;
0277 continue;
0278 }
0279
0280 const float rb2 = B2.maxRadius;
0281
0282 float deltaPhi = deltaPhi0;
0283
0284
0285 if (m_cfg.useEtaBinning) {
0286 const float absDr = std::fabs(rb2 - rb1);
0287 if (m_cfg.useOldTunings) {
0288 deltaPhi = m_cfg.minDeltaPhi + dPhiCoeff * absDr;
0289 } else {
0290 if (absDr < 60.0) {
0291 deltaPhi = 0.002f + 4.33e-4f * ptScale * absDr;
0292 } else {
0293 deltaPhi = 0.015f + 2.2e-4f * ptScale * absDr;
0294 }
0295 }
0296 }
0297
0298 phiSlidingWindow[winIdx].bin = &B2;
0299 phiSlidingWindow[winIdx].hasNodes = true;
0300 phiSlidingWindow[winIdx].deltaPhi = deltaPhi;
0301 ++winIdx;
0302 }
0303
0304
0305 for (std::uint32_t n1Idx = 0; n1Idx < B1.vn.size(); ++n1Idx) {
0306
0307 B1.vFirstEdge[n1Idx] = nEdges;
0308
0309
0310 std::uint16_t numCreatedEdges = 0;
0311
0312 bool isConnected = false;
0313
0314 std::array<std::uint8_t, 16> z0Histo = {};
0315
0316 const std::array<float, 5>& n1pars = B1.params[n1Idx];
0317
0318 const float phi1 = n1pars[2];
0319 const float r1 = n1pars[3];
0320 const float z1 = n1pars[4];
0321
0322
0323 for (auto& slw : phiSlidingWindow) {
0324 if (!slw.hasNodes) {
0325 continue;
0326 }
0327
0328 const GbtsEtaBin& B2 = *slw.bin;
0329
0330 const std::uint32_t lk2 = B2.layerId;
0331
0332 const bool isBarrel2 = (lk2 / 10000) == 8;
0333
0334 const float deltaPhi = slw.deltaPhi;
0335
0336
0337
0338 const float minPhi = phi1 - deltaPhi;
0339 const float maxPhi = phi1 + deltaPhi;
0340
0341
0342 for (std::uint32_t n2PhiIdx = slw.firstIt;
0343 n2PhiIdx < B2.vPhiNodes.size(); ++n2PhiIdx) {
0344 const float phi2 = B2.vPhiNodes[n2PhiIdx].first;
0345
0346 if (phi2 < minPhi) {
0347
0348 slw.firstIt = n2PhiIdx;
0349 continue;
0350 }
0351 if (phi2 > maxPhi) {
0352
0353 break;
0354 }
0355
0356 const std::uint32_t n2Idx = B2.vPhiNodes[n2PhiIdx].second;
0357
0358 const std::uint16_t nodeInfo = B2.vIsConnected[n2Idx];
0359
0360
0361 if ((layerId1 == 80000) && (nodeInfo == 0)) {
0362 continue;
0363 }
0364
0365 const std::uint32_t n2FirstEdge = B2.vFirstEdge[n2Idx];
0366 const std::uint16_t n2NumEdges = B2.vNumEdges[n2Idx];
0367 const std::uint32_t n2LastEdge = n2FirstEdge + n2NumEdges;
0368
0369 const std::array<float, 5>& n2pars = B2.params[n2Idx];
0370
0371 const float r2 = n2pars[3];
0372
0373 const float dr = r2 - r1;
0374
0375 if (dr < m_cfg.minDeltaRadius) {
0376 continue;
0377 }
0378
0379 const float z2 = n2pars[4];
0380
0381 const float dz = z2 - z1;
0382 const float tau = dz / dr;
0383 const float ftau = std::fabs(tau);
0384 if (ftau > 36.0f) {
0385 continue;
0386 }
0387
0388 if (ftau < n1pars[0]) {
0389 continue;
0390 }
0391 if (ftau > n1pars[1]) {
0392 continue;
0393 }
0394
0395 if (ftau < n2pars[0]) {
0396 continue;
0397 }
0398 if (ftau > n2pars[1]) {
0399 continue;
0400 }
0401
0402 const float z0 = z1 - r1 * tau;
0403
0404 if (layerId1 == 80000) {
0405 if (!checkZ0BitMask(nodeInfo, z0, m_cfg.minZ0, z0HistoCoeff)) {
0406 continue;
0407 }
0408 }
0409
0410 if (m_cfg.doubletFilterRZ) {
0411 if (z0 < m_cfg.minZ0 || z0 > m_cfg.maxZ0) {
0412 continue;
0413 }
0414
0415 const float zOuter = z0 + m_cfg.maxOuterRadius * tau;
0416
0417 if (zOuter < cutZMinU || zOuter > cutZMaxU) {
0418 continue;
0419 }
0420 }
0421
0422 const float curv = (phi2 - phi1) / dr;
0423 const float absCurv = std::abs(curv);
0424
0425 if (ftau < 4.0f) {
0426 if (absCurv > maxKappaLowEta) {
0427 continue;
0428 }
0429 } else {
0430 if (absCurv > maxKappaHighEta) {
0431 continue;
0432 }
0433 }
0434
0435 const float expEta = fastHypot(1, tau) - tau;
0436
0437
0438 if (m_cfg.matchBeforeCreate &&
0439 (layerId1 == 80000 || layerId1 == 81000)) {
0440
0441 bool isGood = n2NumEdges <= 2;
0442
0443 if (!isGood) {
0444 const float uat1 = 1.0f / expEta;
0445
0446 for (std::uint32_t n2InIdx = n2FirstEdge; n2InIdx < n2LastEdge;
0447 ++n2InIdx) {
0448 const float tau2 = edgeStorage.at(n2InIdx).p[0];
0449 const float tauRatio = tau2 * uat1 - 1.0f;
0450
0451 if (std::abs(tauRatio) > m_cfg.tauRatioPrecut) {
0452 continue;
0453 }
0454 isGood = true;
0455 break;
0456 }
0457 }
0458
0459 if (!isGood) {
0460 continue;
0461 }
0462 }
0463
0464 const float dPhi2 = curv * r2;
0465 const float dPhi1 = curv * r1;
0466
0467 if (nEdges < m_cfg.nMaxEdges) {
0468 edgeStorage.emplace_back(B1.vn[n1Idx], B2.vn[n2Idx], expEta, curv,
0469 phi1 + dPhi1);
0470
0471 ++numCreatedEdges;
0472
0473 const std::uint32_t outEdgeIdx = nEdges;
0474
0475 const float uat2 = 1.f / expEta;
0476 const float phi2u = phi2 + dPhi2;
0477 const float curv2 = curv;
0478
0479
0480 for (std::uint32_t inEdgeIdx = n2FirstEdge; inEdgeIdx < n2LastEdge;
0481 ++inEdgeIdx) {
0482 GbtsEdge* pS = &(edgeStorage.at(inEdgeIdx));
0483
0484 if (pS->nNei >= gbtsNumSegConns) {
0485 continue;
0486 }
0487
0488 const std::uint32_t lk3 =
0489 m_geometry->layerIdByIndex(pS->n2->layer);
0490
0491 const bool isBarrel3 = (lk3 / 10000) == 8;
0492
0493 const float absTauRatio = std::abs(pS->p[0] * uat2 - 1.0f);
0494 float addTauRatioCorr = 0;
0495
0496 if (m_cfg.useAdaptiveCuts) {
0497 if (isBarrel1 && isBarrel2 && isBarrel3) {
0498 const bool noGap =
0499 ((lk3 - lk2) == 1000) && ((lk2 - layerId1) == 1000);
0500
0501
0502 if (!noGap) {
0503 addTauRatioCorr = m_cfg.tauRatioCorr;
0504 }
0505 } else {
0506 bool mixedTriplet = isBarrel1 && isBarrel2 && !isBarrel3;
0507 if (mixedTriplet) {
0508 addTauRatioCorr = m_cfg.tauRatioCorr;
0509 }
0510 }
0511 }
0512
0513 if (absTauRatio > m_cfg.tauRatioCut + addTauRatioCorr) {
0514 continue;
0515 }
0516
0517 float dPhi = phi2u - pS->p[2];
0518
0519 if (dPhi < -std::numbers::pi_v<float>) {
0520 dPhi += 2 * std::numbers::pi_v<float>;
0521 } else if (dPhi > std::numbers::pi_v<float>) {
0522 dPhi -= 2 * std::numbers::pi_v<float>;
0523 }
0524
0525 if (std::abs(dPhi) > m_cfg.cutDPhiMax) {
0526 continue;
0527 }
0528
0529 const float dcurv = curv2 - pS->p[1];
0530
0531 if (dcurv < -m_cfg.cutDCurvMax || dcurv > m_cfg.cutDCurvMax) {
0532 continue;
0533 }
0534
0535
0536 if (m_cfg.validateTriplets) {
0537
0538 if (isBarrel1 && isBarrel2 && isBarrel3) {
0539 const std::array<const GbtsNode*, 3> candidateTriplet = {
0540 B1.vn[n1Idx], B2.vn[n2Idx], pS->n2};
0541
0542 if (!validateTriplet(candidateTriplet, tripletPtMin,
0543 absTauRatio, m_cfg.tauRatioCut,
0544 options)) {
0545 continue;
0546 }
0547 }
0548 }
0549
0550 pS->vNei[pS->nNei] = outEdgeIdx;
0551 ++pS->nNei;
0552
0553 isConnected = true;
0554
0555
0556
0557 const std::uint32_t z0BinIndex =
0558 static_cast<std::uint32_t>(z0HistoCoeff * (z0 - m_cfg.minZ0));
0559
0560 ++z0Histo[z0BinIndex];
0561
0562 nConnections++;
0563 }
0564 nEdges++;
0565 }
0566 }
0567 }
0568
0569
0570
0571 B1.vNumEdges[n1Idx] = numCreatedEdges;
0572 if (isConnected) {
0573 std::uint16_t z0BitMask = 0x0;
0574
0575 for (std::uint32_t bIdx = 0; bIdx < 16; ++bIdx) {
0576 if (z0Histo[bIdx] == 0) {
0577 continue;
0578 }
0579
0580 z0BitMask |= (1 << bIdx);
0581 }
0582
0583
0584 B1.vIsConnected[n1Idx] = z0BitMask;
0585 }
0586
0587 }
0588 }
0589
0590 if (nEdges >= m_cfg.nMaxEdges) {
0591 ACTS_WARNING(
0592 "Maximum number of graph edges exceeded - possible efficiency loss "
0593 << nEdges);
0594 }
0595 return std::make_pair(nEdges, nConnections);
0596 }
0597
0598 std::int32_t GraphBasedTrackSeeder::runCCA(
0599 const std::uint32_t nEdges, std::vector<GbtsEdge>& edgeStorage) const {
0600 constexpr std::uint32_t maxIter = 15;
0601
0602 std::int32_t maxLevel = 0;
0603
0604 std::uint32_t iter = 0;
0605
0606 std::vector<GbtsEdge*> vOld;
0607
0608 for (std::uint32_t edgeIndex = 0; edgeIndex < nEdges; ++edgeIndex) {
0609 GbtsEdge* pS = &(edgeStorage[edgeIndex]);
0610 if (pS->nNei == 0) {
0611 continue;
0612 }
0613
0614
0615
0616 vOld.push_back(pS);
0617 }
0618
0619 std::vector<GbtsEdge*> vNew;
0620 vNew.reserve(vOld.size());
0621
0622
0623 for (; iter < maxIter; iter++) {
0624 vNew.clear();
0625
0626 for (GbtsEdge* pS : vOld) {
0627 std::int32_t nextLevel = pS->level;
0628
0629 for (std::uint32_t nIdx = 0; nIdx < pS->nNei; ++nIdx) {
0630 const std::uint32_t nextEdgeIdx = pS->vNei[nIdx];
0631
0632 GbtsEdge* pN = &(edgeStorage[nextEdgeIdx]);
0633
0634 if (pS->level == pN->level) {
0635 nextLevel = pS->level + 1;
0636 vNew.push_back(pS);
0637 break;
0638 }
0639 }
0640
0641
0642 pS->next = static_cast<std::int8_t>(nextLevel);
0643 }
0644
0645
0646
0647 std::uint32_t nChanges = 0;
0648
0649 for (auto pS : vNew) {
0650 if (pS->next != pS->level) {
0651 nChanges++;
0652 pS->level = pS->next;
0653 if (maxLevel < pS->level) {
0654 maxLevel = pS->level;
0655 }
0656 }
0657 }
0658
0659 if (nChanges == 0) {
0660 break;
0661 }
0662
0663 vOld.swap(vNew);
0664 vNew.clear();
0665 }
0666
0667 return maxLevel;
0668 }
0669
0670 void GraphBasedTrackSeeder::extractSeedsFromTheGraph(
0671 std::uint32_t maxLevel, std::uint32_t nEdges, std::int32_t nHits,
0672 std::vector<GbtsEdge>& edgeStorage,
0673 std::vector<OutputSeedProperties>& vOutputSeeds,
0674 const GbtsTrackingFilter& filter) const {
0675
0676 std::uint8_t minLevel = 3;
0677
0678 if (m_cfg.lrtMode) {
0679
0680 minLevel = 2;
0681 }
0682
0683 if (maxLevel < minLevel) {
0684 return;
0685 }
0686
0687 std::vector<GbtsEdge*> vChainHeads;
0688
0689 vChainHeads.reserve(nEdges / 2);
0690
0691 for (std::uint32_t edgeIndex = 0; edgeIndex < nEdges; ++edgeIndex) {
0692 GbtsEdge* pS = &(edgeStorage.at(edgeIndex));
0693
0694 if (m_cfg.lrtMode || !m_cfg.addTriplets) {
0695 if (pS->level < minLevel) {
0696 continue;
0697 }
0698 } else {
0699 const float edgeAbsEta = std::abs(-std::log(pS->p[0]));
0700
0701 if (edgeAbsEta > m_cfg.maxAbsEtaAddTripelts) {
0702 if (pS->level < minLevel) {
0703 continue;
0704 }
0705 } else {
0706 if (pS->level < minLevel - 1) {
0707 continue;
0708 }
0709 }
0710 }
0711
0712 vChainHeads.push_back(pS);
0713 }
0714
0715 if (vChainHeads.empty()) {
0716 return;
0717 }
0718
0719 std::ranges::sort(vChainHeads, std::ranges::greater{},
0720 [](const GbtsEdge* e) { return e->level; });
0721
0722
0723
0724 std::vector<SeedCandidateProperties> vSeedCandidates;
0725
0726 vSeedCandidates.reserve(vChainHeads.size());
0727
0728 std::vector<std::pair<float, std::uint32_t>> vArgSort;
0729
0730 vArgSort.reserve(vChainHeads.size());
0731
0732 std::uint32_t seedCounter = 0;
0733
0734 GbtsTrackingFilter::State filterState{};
0735
0736 for (GbtsEdge* pS : vChainHeads) {
0737 if (pS->level == -1) {
0738 continue;
0739 }
0740
0741 GbtsEdgeState rs = filter.followTrack(filterState, edgeStorage, *pS);
0742
0743 if (!rs.initialized) {
0744 continue;
0745 }
0746
0747 const float seedAbsEta = std::abs(-std::log(pS->p[0]));
0748
0749 const std::uint32_t chainLength = static_cast<std::uint32_t>(rs.vs.size());
0750
0751 if (m_cfg.lrtMode || !m_cfg.addTriplets) {
0752 if (chainLength < minLevel) {
0753 continue;
0754 }
0755 } else {
0756 if (seedAbsEta > m_cfg.maxAbsEtaAddTripelts) {
0757 if (chainLength < minLevel) {
0758 continue;
0759 }
0760 } else {
0761 if (chainLength < static_cast<std::uint32_t>(minLevel) - 1u) {
0762 continue;
0763 }
0764 }
0765 }
0766
0767 std::vector<const GbtsNode*> vN;
0768
0769 for (auto sIt = rs.vs.rbegin(); sIt != rs.vs.rend(); ++sIt) {
0770 if (seedAbsEta > m_cfg.edgeMaskMinEta) {
0771
0772 (*sIt)->level = -1;
0773 }
0774
0775 if (sIt == rs.vs.rbegin()) {
0776 vN.push_back((*sIt)->n1);
0777 }
0778
0779 vN.push_back((*sIt)->n2);
0780 }
0781
0782
0783 if (vN.size() < 3) {
0784 continue;
0785 }
0786
0787 const std::uint32_t origSeedSize = vN.size();
0788
0789 const float origSeedQuality = -rs.j / origSeedSize;
0790
0791 std::uint32_t seedSplitFlag = (seedAbsEta < m_cfg.maxSeedSplitEta) &&
0792 (origSeedSize > 3) &&
0793 (origSeedSize <= 5)
0794 ? 1
0795 : 0;
0796
0797
0798 if (seedSplitFlag != 0) {
0799
0800 std::array<std::array<const GbtsNode*, 3>, 3> triplets{};
0801
0802
0803 std::array<float, 3> invRads{};
0804
0805 triplets[0] = {vN[0], vN[origSeedSize / 2], vN[origSeedSize - 1]};
0806
0807
0808 const std::vector<const GbtsNode*> dropOut1(vN.begin() + 1, vN.end());
0809
0810 triplets[1] = {dropOut1[0], dropOut1[(origSeedSize - 1) / 2],
0811 dropOut1[origSeedSize - 2]};
0812
0813 std::vector<const GbtsNode*> dopOut2;
0814
0815 dopOut2.reserve(origSeedSize - 1);
0816
0817 for (std::uint32_t k = 0; k < origSeedSize; k++) {
0818 if (k == origSeedSize / 2) {
0819 continue;
0820 }
0821
0822 dopOut2.emplace_back(vN[k]);
0823 }
0824
0825 triplets[2] = {dopOut2[0], dopOut2[(origSeedSize - 1) / 2],
0826 dopOut2[origSeedSize - 2]};
0827
0828 for (std::uint32_t k = 0; k < invRads.size(); k++) {
0829 invRads[k] = estimateCurvature(triplets[k]);
0830 }
0831
0832 const std::array<float, 3> diffs = {std::abs(invRads[1] - invRads[0]),
0833 std::abs(invRads[2] - invRads[0]),
0834 std::abs(invRads[2] - invRads[1])};
0835
0836 const bool confirmed = diffs[0] < m_cfg.maxInvRadDiff &&
0837 diffs[1] < m_cfg.maxInvRadDiff &&
0838 diffs[2] < m_cfg.maxInvRadDiff;
0839
0840 if (confirmed) {
0841 seedSplitFlag = 0;
0842 }
0843 }
0844
0845 vSeedCandidates.emplace_back(origSeedQuality, 0, vN, seedSplitFlag);
0846
0847 vArgSort.emplace_back(origSeedQuality, seedCounter);
0848
0849 ++seedCounter;
0850 }
0851
0852
0853
0854 std::ranges::sort(vArgSort);
0855
0856 std::vector<std::uint32_t> h2t(nHits + 1, 0);
0857
0858 std::uint32_t trackId = 0;
0859
0860 for (const auto& args : vArgSort) {
0861 const auto& seed = vSeedCandidates[args.second];
0862 ++trackId;
0863
0864
0865 for (const auto& h : seed.spacePoints) {
0866 const std::uint32_t hitId = h->idx + 1;
0867
0868 const std::uint32_t tid = h2t[hitId];
0869
0870
0871 if (tid == 0 || tid > trackId) {
0872
0873 h2t[hitId] = trackId;
0874 }
0875 }
0876 }
0877
0878 std::uint32_t trackIdx = 0;
0879
0880 for (const auto& args : vArgSort) {
0881 const auto& seed = vSeedCandidates[args.second].spacePoints;
0882
0883 const std::uint32_t nTotal = seed.size();
0884
0885 std::uint32_t nOther = 0;
0886
0887 trackId = trackIdx + 1;
0888
0889 ++trackIdx;
0890
0891 for (const auto& h : seed) {
0892 const std::uint32_t hitId = h->idx + 1;
0893
0894 const std::uint32_t tid = h2t[hitId];
0895
0896
0897 if (tid != trackId) {
0898 nOther++;
0899 }
0900 }
0901
0902 if (nOther > m_cfg.hitShareThreshold * nTotal) {
0903
0904 vSeedCandidates[args.second].isClone = -1;
0905 }
0906 }
0907 vOutputSeeds.reserve(vSeedCandidates.size());
0908
0909
0910
0911 for (const auto& args : vArgSort) {
0912 const auto& seed = vSeedCandidates[args.second];
0913
0914 if (seed.isClone != 0) {
0915 continue;
0916 }
0917
0918 const auto& vN = seed.spacePoints;
0919
0920 if (seed.forSeedSplitting == 0) {
0921
0922
0923 std::vector<std::uint32_t> vSpIdx;
0924
0925 vSpIdx.resize(vN.size());
0926
0927 for (std::uint32_t k = 0; k < vSpIdx.size(); k++) {
0928 vSpIdx[k] = vN[k]->idx;
0929 }
0930
0931 vOutputSeeds.emplace_back(seed.seedQuality, vSpIdx);
0932
0933 continue;
0934 }
0935
0936
0937
0938 const std::uint32_t seedSize = vN.size();
0939
0940 const std::array<std::size_t, 2> indices2drop = {
0941 0, seedSize / 2ul};
0942
0943 for (const auto& skipIdx : indices2drop) {
0944 std::vector<std::uint32_t> newSeed;
0945
0946 newSeed.reserve(seedSize - 1);
0947
0948 for (std::uint32_t k = 0; k < seedSize; k++) {
0949 if (k == skipIdx) {
0950 continue;
0951 }
0952
0953 newSeed.emplace_back(vN[k]->idx);
0954 }
0955
0956 vOutputSeeds.emplace_back(seed.seedQuality, newSeed);
0957 }
0958 }
0959 }
0960
0961 bool GraphBasedTrackSeeder::checkZ0BitMask(const std::uint16_t z0BitMask,
0962 const float z0, const float minZ0,
0963 const float z0HistoCoeff) const {
0964 if (z0BitMask == 0) {
0965 return true;
0966 }
0967
0968 const float dz = z0 - minZ0;
0969 const std::int32_t z0BinIndex = static_cast<std::int32_t>(z0HistoCoeff * dz);
0970
0971 if (((z0BitMask >> z0BinIndex) & 1) != 0) {
0972 return true;
0973 }
0974
0975
0976
0977 const float z0Resolution = 2.5;
0978
0979 const float dzm = dz - z0Resolution;
0980
0981 std::int32_t nextBin = static_cast<std::int32_t>(z0HistoCoeff * dzm);
0982
0983 if (nextBin >= 0 && nextBin != z0BinIndex) {
0984 if (((z0BitMask >> nextBin) & 1) != 0) {
0985 return true;
0986 }
0987 }
0988
0989 const float dzp = dz + z0Resolution;
0990
0991 nextBin = static_cast<std::int32_t>(z0HistoCoeff * dzp);
0992
0993 if (nextBin < 16 && nextBin != z0BinIndex) {
0994 if (((z0BitMask >> nextBin) & 1) != 0) {
0995 return true;
0996 }
0997 }
0998
0999 return false;
1000 }
1001
1002 float GraphBasedTrackSeeder::estimateCurvature(
1003 const std::array<const GbtsNode*, 3>& nodes) const {
1004
1005
1006 std::array<float, 2> u{};
1007 std::array<float, 2> v{};
1008
1009 const float x0 = nodes[2]->x;
1010 const float y0 = nodes[2]->y;
1011
1012 const float r0 = nodes[2]->r;
1013
1014 const float cosA = x0 / r0;
1015
1016 const float sinA = y0 / r0;
1017
1018 for (std::uint32_t k = 0; k < 2; k++) {
1019 const float dx = nodes[k]->x - x0;
1020
1021 const float dy = nodes[k]->y - y0;
1022
1023 const float r2Inv = 1.0 / (dx * dx + dy * dy);
1024
1025 const float xn = dx * cosA + dy * sinA;
1026
1027 const float yn = -dx * sinA + dy * cosA;
1028
1029 u[k] = xn * r2Inv;
1030 v[k] = yn * r2Inv;
1031 }
1032
1033 const float du = u[0] - u[1];
1034
1035 if (du == 0.0) {
1036 return 0.0;
1037 }
1038
1039 const float A = (v[0] - v[1]) / du;
1040
1041 const float B = v[1] - A * u[1];
1042
1043
1044 return B / std::sqrt(1 + A * A);
1045 }
1046
1047 bool GraphBasedTrackSeeder::validateTriplet(
1048 const std::array<const GbtsNode*, 3> candidateTriplet,
1049 const float tripletMinPt, const float tauRatio, const float tauRatioCut,
1050 const Options& options) const {
1051
1052
1053 std::array<float, 2> u{};
1054 std::array<float, 2> v{};
1055
1056 const float x0 = candidateTriplet[1]->x;
1057 const float y0 = candidateTriplet[1]->y;
1058
1059 const float r0 = candidateTriplet[1]->r;
1060
1061 const float cosA = x0 / r0;
1062
1063 const float sinA = y0 / r0;
1064
1065 for (std::uint32_t k = 0; k < 2; k++) {
1066 const std::uint32_t spIdx = (k == 1) ? 2 : k;
1067
1068 const float dx = candidateTriplet[spIdx]->x - x0;
1069
1070 const float dy = candidateTriplet[spIdx]->y - y0;
1071
1072 const float r2Inv = 1.0f / (dx * dx + dy * dy);
1073
1074 const float xn = dx * cosA + dy * sinA;
1075
1076 const float yn = -dx * sinA + dy * cosA;
1077
1078 u[k] = xn * r2Inv;
1079 v[k] = yn * r2Inv;
1080 }
1081
1082 const float du = u[0] - u[1];
1083
1084 if (du == 0.0) {
1085 return false;
1086 }
1087
1088 const float A = (v[0] - v[1]) / du;
1089
1090 const float B = v[1] - A * u[1];
1091
1092 const float d0 = r0 * (B * r0 - A);
1093
1094 if (std::abs(d0) > m_cfg.d0Max) {
1095 return false;
1096 }
1097
1098 if (B != 0.0) {
1099
1100 const float R = std::sqrt(1 + A * A) / B;
1101
1102
1103 const float pT = std::abs(options.bFieldInZ * R / 2);
1104
1105 if (pT < tripletMinPt) {
1106 return false;
1107 }
1108
1109 if (pT > 5 * tripletMinPt) {
1110
1111 if (tauRatio > 0.9f * tauRatioCut) {
1112 return false;
1113 }
1114 }
1115 }
1116
1117 return true;
1118 }
1119
1120 }