Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-13 09:21:26

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Seeding/SeedFinderGbts.hpp"
0010 
0011 #include "Acts/Seeding/GbtsTrackingFilter.hpp"
0012 #include "Acts/Seeding/SeedFinderGbtsConfig.hpp"
0013 
0014 #include <algorithm>
0015 #include <cmath>
0016 #include <numbers>
0017 #include <utility>
0018 #include <vector>
0019 
0020 namespace Acts::Experimental {
0021 
0022 SeedFinderGbts::SeedFinderGbts(
0023     SeedFinderGbtsConfig config, const GbtsGeometry* gbtsGeo,
0024     const std::vector<TrigInDetSiLayer>* layerGeometry,
0025     std::unique_ptr<const Acts::Logger> logger)
0026     : m_config(std::move(config)),
0027       m_geo(gbtsGeo),
0028       m_storage(std::make_unique<GNN_DataStorage>(*m_geo)),
0029       m_layerGeometry(layerGeometry),
0030       m_logger(std::move(logger)) {}
0031 
0032 SeedContainer2 SeedFinderGbts::CreateSeeds(
0033     const RoiDescriptor& roi,
0034     const SPContainerComponentsType& SpContainerComponents, int max_layers) {
0035   SeedContainer2 SeedContainer;
0036   std::vector<std::vector<GNN_Node>> node_storage =
0037       CreateNodes(SpContainerComponents, max_layers);
0038   unsigned int nPixelLoaded = 0;
0039   unsigned int nStripLoaded = 0;
0040 
0041   for (std::size_t l = 0; l < node_storage.size(); l++) {
0042     const std::vector<GNN_Node>& nodes = node_storage[l];
0043 
0044     if (nodes.empty()) {
0045       continue;
0046     }
0047 
0048     bool is_pixel = true;
0049     if (is_pixel) {  // placeholder for now until strip hits are added in
0050 
0051       nPixelLoaded += m_storage->loadPixelGraphNodes(l, nodes, m_config.useML);
0052 
0053     } else {
0054       nStripLoaded += m_storage->loadStripGraphNodes(l, nodes);
0055     }
0056   }
0057   ACTS_DEBUG("Loaded " << nPixelLoaded << " pixel spacepoints and "
0058                        << nStripLoaded << " strip spacepoints");
0059 
0060   m_storage->sortByPhi();
0061 
0062   m_storage->initializeNodes(m_config.useML);
0063 
0064   m_config.phiSliceWidth = 2 * std::numbers::pi / m_config.nMaxPhiSlice;
0065   m_storage->generatePhiIndexing(1.5 * m_config.phiSliceWidth);
0066 
0067   std::vector<GNN_Edge> edgeStorage;
0068 
0069   std::pair<int, int> graphStats = buildTheGraph(roi, m_storage, edgeStorage);
0070 
0071   ACTS_DEBUG("Created graph with " << graphStats.first << " edges and "
0072                                    << graphStats.second << " edge links");
0073 
0074   int maxLevel = runCCA(graphStats.first, edgeStorage);
0075 
0076   ACTS_DEBUG("Reached Level " << maxLevel << " after GNN iterations");
0077 
0078   int minLevel = 3;  // a triplet + 2 confirmation
0079 
0080   if (m_config.LRTmode) {
0081     minLevel = 2;  // a triplet + 1 confirmation
0082   }
0083 
0084   std::vector<GNN_Edge*> vSeeds;
0085 
0086   vSeeds.reserve(graphStats.first / 2);
0087 
0088   for (int edgeIndex = 0; edgeIndex < graphStats.first; edgeIndex++) {
0089     GNN_Edge* pS = &(edgeStorage.at(edgeIndex));
0090 
0091     if (pS->m_level < minLevel) {
0092       continue;
0093     }
0094 
0095     vSeeds.push_back(pS);
0096   }
0097 
0098   std::sort(vSeeds.begin(), vSeeds.end(), GNN_Edge::CompareLevel());
0099 
0100   // backtracking
0101 
0102   GbtsTrackingFilter tFilter(*m_layerGeometry, edgeStorage, m_config);
0103 
0104   for (auto pS : vSeeds) {
0105     if (pS->m_level == -1) {
0106       continue;
0107     }
0108 
0109     GbtsEdgeState rs(false);  // this is in tracking filter as well
0110 
0111     tFilter.followTrack(pS, rs);
0112 
0113     if (!rs.m_initialized) {
0114       continue;
0115     }
0116 
0117     if (static_cast<int>(rs.m_vs.size()) < minLevel) {
0118       continue;
0119     }
0120 
0121     std::vector<const GNN_Node*> vN;
0122 
0123     for (std::vector<GNN_Edge*>::reverse_iterator sIt = rs.m_vs.rbegin();
0124          sIt != rs.m_vs.rend(); ++sIt) {
0125       (*sIt)->m_level = -1;  // mark as collected
0126 
0127       if (sIt == rs.m_vs.rbegin()) {
0128         vN.push_back((*sIt)->m_n1);
0129       }
0130 
0131       vN.push_back((*sIt)->m_n2);
0132     }
0133 
0134     if (vN.size() < 3) {
0135       continue;
0136     }
0137 
0138     // add to seed container:
0139     std::vector<SpacePointIndex2> Sp_Indexes{};
0140     Sp_Indexes.reserve(vN.size());
0141 
0142     for (std::size_t i = 0; i < vN.size(); i++) {
0143       Sp_Indexes.emplace_back(vN.at(i)->sp_idx());
0144     }
0145 
0146     auto seed = SeedContainer.createSeed();
0147     seed.assignSpacePointIndices(Sp_Indexes);
0148   }
0149 
0150   ACTS_DEBUG("GBTS created " << SeedContainer.size() << " seeds");
0151 
0152   return SeedContainer;
0153 }
0154 
0155 std::vector<std::vector<SeedFinderGbts::GNN_Node>> SeedFinderGbts::CreateNodes(
0156     const auto& container, int MaxLayers) {
0157   std::vector<std::vector<SeedFinderGbts::GNN_Node>> node_storage(MaxLayers);
0158   // reserve for better efficiency
0159 
0160   for (auto& v : node_storage) {
0161     v.reserve(100000);
0162   }
0163 
0164   for (auto sp : std::get<0>(container)) {
0165     // for every sp in container,
0166     // add its variables to node_storage organised by layer
0167     int layer = sp.extra(std::get<1>(container));
0168 
0169     // add node to storage
0170     SeedFinderGbts::GNN_Node& node = node_storage[layer].emplace_back(layer);
0171 
0172     // fill the node with spacepoint variables
0173 
0174     node.m_x = sp.x();
0175     node.m_y = sp.y();
0176     node.m_z = sp.z();
0177     node.m_r = sp.r();
0178     node.m_phi = sp.phi();
0179     node.m_idx =
0180         sp.index();  // change node so that is uses SpacePointIndex2 (doesn't
0181                      // affect code but i guess it looks cleaner)
0182     node.m_pcw = sp.extra(std::get<2>(container));
0183   }
0184 
0185   return node_storage;
0186 }
0187 
0188 std::pair<int, int> SeedFinderGbts::buildTheGraph(
0189     const RoiDescriptor& roi, const std::unique_ptr<GbtsDataStorage>& storage,
0190     std::vector<GbtsEdge>& edgeStorage) const {
0191   // phi cut for triplets
0192   const float cut_dphi_max = m_config.LRTmode ? 0.07f : 0.012f;
0193   // curv cut for triplets
0194   const float cut_dcurv_max = m_config.LRTmode ? 0.015f : 0.001f;
0195   // tau cut for doublets and triplets
0196   const float cut_tau_ratio_max =
0197       m_config.LRTmode ? 0.015f : static_cast<float>(m_config.tau_ratio_cut);
0198   const float min_z0 = m_config.LRTmode ? -600.0 : roi.zedMinus();
0199   const float max_z0 = m_config.LRTmode ? 600.0 : roi.zedPlus();
0200   const float min_deltaPhi = m_config.LRTmode ? 0.01f : 0.001f;
0201 
0202   // used to calculate Z cut on doublets
0203   const float maxOuterRadius = m_config.LRTmode ? 1050.0 : 550.0;
0204 
0205   const float cut_zMinU = min_z0 + maxOuterRadius * roi.dzdrMinus();
0206   const float cut_zMaxU = max_z0 + maxOuterRadius * roi.dzdrPlus();
0207 
0208   // correction due to limited pT resolution
0209   float tripletPtMin = 0.8 * m_config.minPt;
0210   // to re-scale original tunings done for the 900 MeV pT cut
0211   const float pt_scale = 900.0 / m_config.minPt;
0212 
0213   float maxCurv = m_config.ptCoeff / tripletPtMin;
0214 
0215   float maxKappa_high_eta =
0216       m_config.LRTmode ? 1.0 * maxCurv : std::sqrt(0.8) * maxCurv;
0217   float maxKappa_low_eta =
0218       m_config.LRTmode ? 1.0 * maxCurv : std::sqrt(0.6) * maxCurv;
0219 
0220   // new settings for curvature cuts
0221   if (!m_config.useOldTunings && !m_config.LRTmode) {
0222     maxKappa_high_eta = 4.75e-4f * pt_scale;
0223     maxKappa_low_eta = 3.75e-4f * pt_scale;
0224   }
0225 
0226   const float dphi_coeff = m_config.LRTmode ? 1.0 * maxCurv : 0.68 * maxCurv;
0227 
0228   // the default sliding window along phi
0229   float deltaPhi = 0.5f * m_config.phiSliceWidth;
0230 
0231   unsigned int nConnections = 0;
0232 
0233   edgeStorage.reserve(m_config.nMaxEdges);
0234 
0235   int nEdges = 0;
0236 
0237   for (const auto& bg : m_geo->bin_groups()) {  // loop over bin groups
0238 
0239     GbtsEtaBin& B1 = storage->getEtaBin(bg.first);
0240 
0241     if (B1.empty()) {
0242       continue;
0243     }
0244 
0245     float rb1 = B1.getMinBinRadius();
0246 
0247     for (const auto& b2_idx : bg.second) {
0248       const GbtsEtaBin& B2 = storage->getEtaBin(b2_idx);
0249 
0250       if (B2.empty()) {
0251         continue;
0252       }
0253 
0254       float rb2 = B2.getMaxBinRadius();
0255 
0256       if (m_config.useEtaBinning) {
0257         float abs_dr = std::fabs(rb2 - rb1);
0258         if (m_config.useOldTunings) {
0259           deltaPhi = min_deltaPhi + dphi_coeff * abs_dr;
0260         } else {
0261           if (abs_dr < 60.0) {
0262             deltaPhi = 0.002f + 4.33e-4f * pt_scale * abs_dr;
0263           } else {
0264             deltaPhi = 0.015f + 2.2e-4f * pt_scale * abs_dr;
0265           }
0266         }
0267       }
0268 
0269       unsigned int first_it = 0;
0270 
0271       for (unsigned int n1Idx = 0; n1Idx < B1.m_vn.size();
0272            n1Idx++) {  // loop over nodes in Layer 1
0273 
0274         std::vector<unsigned int>& v1In = B1.m_in[n1Idx];
0275 
0276         if (v1In.size() >= MAX_SEG_PER_NODE) {
0277           continue;
0278         }
0279 
0280         const std::array<float, 5>& n1pars = B1.m_params[n1Idx];
0281 
0282         float phi1 = n1pars[2];
0283         float r1 = n1pars[3];
0284         float z1 = n1pars[4];
0285 
0286         // sliding window phi1 +/- deltaPhi
0287 
0288         float minPhi = phi1 - deltaPhi;
0289         float maxPhi = phi1 + deltaPhi;
0290 
0291         for (unsigned int n2PhiIdx = first_it; n2PhiIdx < B2.m_vPhiNodes.size();
0292              n2PhiIdx++) {  // sliding window over nodes in Layer 2
0293 
0294           float phi2 = B2.m_vPhiNodes[n2PhiIdx].first;
0295 
0296           if (phi2 < minPhi) {
0297             first_it = n2PhiIdx;
0298             continue;
0299           }
0300           if (phi2 > maxPhi) {
0301             break;
0302           }
0303 
0304           unsigned int n2Idx = B2.m_vPhiNodes[n2PhiIdx].second;
0305 
0306           const std::vector<unsigned int>& v2In = B2.m_in[n2Idx];
0307 
0308           if (v2In.size() >= MAX_SEG_PER_NODE) {
0309             continue;
0310           }
0311 
0312           const std::array<float, 5>& n2pars = B2.m_params[n2Idx];
0313 
0314           float r2 = n2pars[3];
0315 
0316           float dr = r2 - r1;
0317 
0318           if (dr < m_config.minDeltaRadius) {
0319             continue;
0320           }
0321 
0322           float z2 = n2pars[4];
0323 
0324           float dz = z2 - z1;
0325           float tau = dz / dr;
0326           float ftau = std::fabs(tau);
0327           if (ftau > 36.0) {
0328             continue;
0329           }
0330 
0331           if (ftau < n1pars[0]) {
0332             continue;
0333           }
0334           if (ftau > n1pars[1]) {
0335             continue;
0336           }
0337 
0338           if (ftau < n2pars[0]) {
0339             continue;
0340           }
0341           if (ftau > n2pars[1]) {
0342             continue;
0343           }
0344 
0345           if (m_config.doubletFilterRZ) {
0346             float z0 = z1 - r1 * tau;
0347 
0348             if (z0 < min_z0 || z0 > max_z0) {
0349               continue;
0350             }
0351 
0352             float zouter = z0 + maxOuterRadius * tau;
0353 
0354             if (zouter < cut_zMinU || zouter > cut_zMaxU) {
0355               continue;
0356             }
0357           }
0358 
0359           float curv = (phi2 - phi1) / dr;
0360           float abs_curv = std::abs(curv);
0361 
0362           if (ftau < 4.0) {  // eta = 2.1
0363             if (abs_curv > maxKappa_low_eta) {
0364               continue;
0365             }
0366           } else {
0367             if (abs_curv > maxKappa_high_eta) {
0368               continue;
0369             }
0370           }
0371 
0372           float exp_eta = std::sqrt(1 + tau * tau) - tau;
0373 
0374           if (m_config.matchBeforeCreate) {  // match edge candidate against
0375                                              // edges incoming to n2
0376 
0377             bool isGood = v2In.size() <=
0378                           2;  // we must have enough incoming edges to decide
0379 
0380             if (!isGood) {
0381               float uat_1 = 1.0f / exp_eta;
0382 
0383               for (const auto& n2_in_idx : v2In) {
0384                 float tau2 = edgeStorage.at(n2_in_idx).m_p[0];
0385                 float tau_ratio = tau2 * uat_1 - 1.0f;
0386 
0387                 if (std::fabs(tau_ratio) > cut_tau_ratio_max) {  // bad match
0388                   continue;
0389                 }
0390                 isGood = true;  // good match found
0391                 break;
0392               }
0393             }
0394 
0395             if (!isGood) {  // no match found, skip creating [n1 <- n2] edge
0396               continue;
0397             }
0398           }
0399 
0400           float dPhi2 = curv * r2;
0401           float dPhi1 = curv * r1;
0402 
0403           if (nEdges < m_config.nMaxEdges) {
0404             edgeStorage.emplace_back(B1.m_vn[n1Idx], B2.m_vn[n2Idx], exp_eta,
0405                                      curv, phi1 + dPhi1);
0406 
0407             if (v1In.size() < MAX_SEG_PER_NODE) {
0408               v1In.push_back(nEdges);
0409             }
0410 
0411             int outEdgeIdx = nEdges;
0412 
0413             float uat_2 = 1 / exp_eta;
0414             float Phi2 = phi2 + dPhi2;
0415             float curv2 = curv;
0416 
0417             for (const auto& inEdgeIdx :
0418                  v2In) {  // looking for neighbours of the new edge
0419 
0420               GbtsEdge* pS = &(edgeStorage.at(inEdgeIdx));
0421 
0422               if (pS->m_nNei >= N_SEG_CONNS) {
0423                 continue;
0424               }
0425 
0426               float tau_ratio = pS->m_p[0] * uat_2 - 1.0f;
0427 
0428               if (std::abs(tau_ratio) > cut_tau_ratio_max) {  // bad match
0429                 continue;
0430               }
0431 
0432               float dPhi = Phi2 - pS->m_p[2];
0433 
0434               if (dPhi < -std::numbers::pi) {
0435                 dPhi += 2 * std::numbers::pi;
0436               } else if (dPhi > std::numbers::pi) {
0437                 dPhi -= 2 * std::numbers::pi;
0438               }
0439 
0440               if (dPhi < -cut_dphi_max || dPhi > cut_dphi_max) {
0441                 continue;
0442               }
0443 
0444               float dcurv = curv2 - pS->m_p[1];
0445 
0446               if (dcurv < -cut_dcurv_max || dcurv > cut_dcurv_max) {
0447                 continue;
0448               }
0449 
0450               pS->m_vNei[pS->m_nNei++] = outEdgeIdx;
0451 
0452               nConnections++;
0453             }
0454             nEdges++;
0455           }
0456         }  // loop over n2 (outer) nodes
0457       }  // loop over n1 (inner) nodes
0458     }  // loop over bins in Layer 2
0459   }  // loop over bin groups
0460 
0461   if (nEdges >= m_config.nMaxEdges) {
0462     ACTS_WARNING(
0463         "Maximum number of graph edges exceeded - possible efficiency loss "
0464         << nEdges);
0465   }
0466   return std::make_pair(nEdges, nConnections);
0467 }
0468 
0469 int SeedFinderGbts::runCCA(int nEdges,
0470                            std::vector<GbtsEdge>& edgeStorage) const {
0471   const int maxIter = 15;
0472 
0473   int maxLevel = 0;
0474 
0475   int iter = 0;
0476 
0477   std::vector<GbtsEdge*> v_old;
0478 
0479   for (int edgeIndex = 0; edgeIndex < nEdges; edgeIndex++) {
0480     GbtsEdge* pS = &(edgeStorage[edgeIndex]);
0481     if (pS->m_nNei == 0) {
0482       continue;
0483     }
0484 
0485     v_old.push_back(pS);  // TO-DO: increment level for segments as they already
0486                           // have at least one neighbour
0487   }
0488 
0489   for (; iter < maxIter; iter++) {
0490     // generate proposals
0491     std::vector<GbtsEdge*> v_new;
0492     v_new.clear();
0493     v_new.reserve(v_old.size());
0494 
0495     for (auto pS : v_old) {
0496       int next_level = pS->m_level;
0497 
0498       for (int nIdx = 0; nIdx < pS->m_nNei; nIdx++) {
0499         unsigned int nextEdgeIdx = pS->m_vNei[nIdx];
0500 
0501         GbtsEdge* pN = &(edgeStorage[nextEdgeIdx]);
0502 
0503         if (pS->m_level == pN->m_level) {
0504           next_level = pS->m_level + 1;
0505           v_new.push_back(pS);
0506           break;
0507         }
0508       }
0509 
0510       pS->m_next = next_level;  // proposal
0511     }
0512 
0513     // update
0514 
0515     int nChanges = 0;
0516 
0517     for (auto pS : v_new) {
0518       if (pS->m_next != pS->m_level) {
0519         nChanges++;
0520         pS->m_level = pS->m_next;
0521         if (maxLevel < pS->m_level) {
0522           maxLevel = pS->m_level;
0523         }
0524       }
0525     }
0526 
0527     if (nChanges == 0) {
0528       break;
0529     }
0530 
0531     v_old = std::move(v_new);
0532     v_new.clear();
0533   }
0534 
0535   return maxLevel;
0536 }
0537 
0538 }  // namespace Acts::Experimental