Warning, /acts/docs/groups/pattern_recog/gbts.md is written in an unsupported language. File is not indexed.
0001 @defgroup gbts Graph-Based Track Seeding
0002 @ingroup seeding
0003 @brief Seeding by building and filtering a graph of hit doublets
0004
0005 > [!tip]
0006 > This page documents @ref Acts::Experimental::GraphBasedTrackSeeder "GBTS" as
0007 > implemented in ACTS today. GBTS is an alternative to the classical
0008 > triplet-based @ref seeding, not a layer on top of it — the two are independent
0009 > entry points producing the same @ref Acts::SeedContainer.
0010
0011 ## Why a graph?
0012
0013 Classical ACTS seeding (@ref seeding) enumerates *triplets* of space points from
0014 a binned grid and cuts on the helix they describe. The combinatorics of that
0015 enumeration grow steeply with occupancy, and every triplet is judged in
0016 isolation.
0017
0018 GBTS inverts the order. It first builds a *graph*: nodes are space points, and a
0019 directed edge joins two space points on connected detector layers whenever the
0020 pair passes a set of cheap two-point cuts. Only then does it look for structure
0021 in that graph — long chains of mutually compatible edges — and turn the best
0022 chains into seeds. The expensive per-candidate work is therefore done once per
0023 *edge* rather than once per triplet, and the chain length itself becomes a
0024 quality signal.
0025
0026 The workflow has four stages, each documented below:
0027
0028 1. @ref gbts-nodes — sort the space points into eta/phi-ordered graph nodes.
0029 2. @ref gbts-graph — create edges between compatible node pairs and link
0030 compatible edges to each other.
0031 3. @ref gbts-cca — propagate a "level" through the edge graph to find the
0032 longest chains.
0033 4. @ref gbts-extraction — follow the best chains through a Kalman-like filter
0034 and emit seeds.
0035
0036 ## Geometry and layer connections {#gbts-geometry}
0037
0038 GBTS does not use the ACTS tracking geometry. It works on its own lightweight
0039 description: a flat list of `GbtsLayer` logical layers, each subdivided into
0040 **eta bins**.
0041
0042 @ref Acts::Experimental::GbtsLayerDescription gives a layer its ID, its type
0043 (barrel or endcap), its sensor technology and its extent. For a barrel layer
0044 `refCoord` is the radius and the bounds are in @f$z@f$; for an endcap it is the
0045 other way round. The ID is the caller's own numbering and is never decoded.
0046
0047 Which layer pairs may be joined by an edge is a list of
0048 @ref Acts::Experimental::GbtsLayerConnection, each naming a source (outer) and a
0049 destination (inner) layer. @ref Acts::Experimental::GbtsGeometry combines the
0050 layer descriptions with those connections and precomputes, for every pair of
0051 connected layers, which *eta bin* pairs are geometrically compatible with the
0052 allowed @f$z_0@f$ range. The result is a **bin group** list — one inner bin
0053 together with all outer bins it may connect to — which is kept internal to the
0054 geometry and serves as the graph builder's iteration schedule, ordered so that
0055 outer bins are processed before the inner bins that depend on them.
0056
0057 > [!note]
0058 > The connections are trained offline rather than written by hand.
0059 > @ref Acts::Experimental::GbtsLayerConnectionTool accumulates layer-pair
0060 > statistics from simulated tracks; the
0061 > `Examples/Scripts/Python/gbts_layer_connection_training.py` script drives it.
0062 > `ActsExamples::GraphBasedSeedingAlgorithm` reads the resulting table, in
0063 > ATLAS' connector file format, and hands the pairs it lists to the geometry.
0064
0065 ## Graph nodes {#gbts-nodes}
0066
0067 @ref Acts::Experimental::GbtsNodeStorage holds the graph nodes. Space points are
0068 fed in one at a time through `insert`, which takes **plain scalars** rather than
0069 an ACTS container:
0070
0071 @snippet{trimleft} include/Acts/Seeding/GbtsNodeStorage.hpp gbts insert
0072
0073 As with @ref Acts::CylindricalSpacePointGrid, an experiment can therefore fill
0074 the storage straight from its own space point EDM. Overloads exist for callers
0075 that already have @f$r@f$ and @f$\phi@f$, and for an
0076 @ref Acts::ConstSpacePointProxy together with the columns carrying the layer
0077 index, cluster width and local @f$y@f$ position.
0078
0079 `insert` assigns the node to an eta bin via `GbtsLayer::getEtaBin` and buffers
0080 it. `finalize` then sorts each bin by @f$\phi@f$ and materialises the nodes into
0081 a space point container ordered by eta bin and then by @f$\phi@f$, so that every
0082 eta bin is **one contiguous range of node indices**. A node index is therefore
0083 all that the rest of the algorithm needs to pass around.
0084
0085 The per-node data the graph builder reads lives in dynamic columns on that same
0086 container. It is packed rather than split into one array per field, because the
0087 innermost loop reads all of it together:
0088
0089 @snippet{trimleft} include/Acts/Seeding/detail/GbtsGraphTypes.hpp gbts node params
0090
0091 @f$\tau = \cot\theta@f$. The infinite defaults disable the @f$\tau@f$ cut
0092 entirely; only the optional machine-learning lookup table narrows them (see
0093 @ref gbts-ml). Alongside it sits the bookkeeping the graph builder writes:
0094
0095 @snippet{trimleft} include/Acts/Seeding/detail/GbtsGraphTypes.hpp gbts node edge info
0096
0097 Each eta bin carries its node range plus the @f$\phi@f$ index used by the sliding
0098 window. The @f$\phi@f$ index duplicates entries shifted by @f$\pm 2\pi@f$ near the
0099 wrap-around, so the window never has to handle wrapping:
0100
0101 @snippet{trimleft} include/Acts/Seeding/detail/GbtsGraphTypes.hpp gbts eta bin info
0102
0103 ## Building the graph {#gbts-graph}
0104
0105 The builder walks the bin groups from @ref gbts-geometry. For each inner bin it
0106 prepares one **sliding window** in @f$\phi@f$ per connected outer bin, whose
0107 half-width grows with the radial separation of the two bins — the further apart
0108 they are, the more a low-@f$p_T@f$ track can bend between them. It then loops
0109 over the inner nodes, and for each one scans only the outer nodes inside the
0110 window.
0111
0112 A candidate pair @f$(n_1, n_2)@f$ becomes an edge if it survives, in order:
0113
0114 | Cut | Meaning |
0115 | --- | --- |
0116 | @f$\Delta r > @f$ `minDeltaRadius` | the two hits are radially separated enough for @f$\tau@f$ to be meaningful |
0117 | @f$\lvert\tau\rvert < @f$ `maxAbsTau` | within the detector's angular acceptance |
0118 | @f$\tau@f$ inside both nodes' windows | per-node acceptance from the ML lookup table |
0119 | @f$z_0@f$ inside `[minZ0, maxZ0]`, and @f$z@f$ at the outer radius inside the ROI | the pair points back to the luminous region |
0120 | @f$\lvert\kappa\rvert@f$ below an @f$\eta@f$-dependent bound | consistent with the @f$p_T@f$ threshold |
0121
0122 with @f$\tau = \Delta z/\Delta r@f$, @f$z_0 = z_1 - r_1\tau@f$ and the curvature
0123 proxy @f$\kappa = (\phi_2-\phi_1)/\Delta r@f$.
0124
0125 Surviving pairs are appended to a flat edge array:
0126
0127 @snippet{trimleft} include/Acts/Seeding/detail/GbtsGraphTypes.hpp gbts edge
0128
0129 The three fit parameters `p` are @f$\{\exp(-\eta),\ \kappa,\ \phi_1 + \kappa
0130 r_1\}@f$.
0131
0132 Because the inner node's edges are written contiguously, the edges *incoming* to
0133 a node form a contiguous range, recorded in that node's `GbtsNodeEdgeInfo`.
0134 Immediately after creating an edge @f$(n_1, n_2)@f$, the builder scans the edges
0135 incoming to @f$n_2@f$ — that is, edges @f$(n_2, n_3)@f$ — and links the two
0136 whenever the implied triplet is consistent: the @f$\tau@f$ ratio, the @f$\phi@f$
0137 continuation and the curvature difference must all agree within tolerance. For
0138 pixel-barrel triplets an optional
0139 @ref Acts::Experimental::GraphBasedTrackSeeder "validateTriplets" step also fits
0140 a circle through the three points and cuts on @f$d_0@f$ and @f$p_T@f$. Each
0141 edge stores up to `kGbtsMaxEdgeNeighbours` (6) such neighbours.
0142
0143 Two further cuts apply on the innermost pixel barrel layers, where the
0144 combinatorics are worst:
0145
0146 - `matchBeforeCreate` (off by default) demands the @f$\tau@f$ half of the
0147 triplet test *before* the edge exists: @f$n_2@f$ must already carry an
0148 incoming edge whose @f$\tau@f$ agrees with the candidate's within
0149 `tauRatioPrecut`. A node with two or fewer incoming edges passes
0150 unconditionally, there being too little evidence to reject it.
0151 - Every inner node accumulates a 16-bit @f$z_0@f$ **histogram bitmask** of its
0152 confirmed edges. On the innermost layer that mask rejects candidates whose
0153 @f$z_0@f$ falls in an empty bin, and nodes with no connections at all are
0154 skipped outright.
0155
0156 ## Connected component analysis {#gbts-cca}
0157
0158 With the edge graph built, a cellular automaton assigns each edge a **level**:
0159 the length of the longest chain of linked edges ending at it. All edges start at
0160 level 1; in each iteration an edge whose level equals that of one of its
0161 neighbours proposes an increment, and the proposals are committed at the end of
0162 the iteration. The sweep repeats until nothing changes, or for at most 15
0163 iterations.
0164
0165 The level is the chain-length signal that drives extraction: an edge at level
0166 @f$L@f$ is the head of a chain spanning @f$L+1@f$ space points.
0167
0168 ## Seed extraction {#gbts-extraction}
0169
0170 Edges whose level clears the minimum chain length become **chain heads**, sorted
0171 by level so the longest chains are collected first. Each head is then followed
0172 back through the graph by @ref Acts::Experimental::GbtsTrackingFilter.
0173
0174 The filter is a small Kalman filter over the chain. It carries a state of two
0175 independent parts — a quadratic in the bending plane and a linear @f$z@f$ versus
0176 @f$r@f$ model — and at each step extrapolates to the next node, forms a
0177 @f$\chi^2@f$ residual for each part and rejects the branch if either exceeds its
0178 threshold (`maxDChi2X`, `maxDChi2Y`).
0179
0180 Every accepted hit adds a fixed reward `addHit` to the branch score, minus its
0181 two @f$\chi^2@f$ increments weighted by `weightX` and `weightY`. The score
0182 therefore counts the hits on the chain, discounted by how badly they fit the
0183 circle and the @f$z@f$ versus @f$r@f$ line. Where an edge has several
0184 neighbours the filter *branches*, recursing into each; the branch with the best
0185 accumulated score wins.
0186
0187 The result is a set of seed candidates. These are reduced in two passes:
0188
0189 - **Clone removal.** Candidates are ranked by quality, and each space point is
0190 assigned to the best candidate claiming it. A candidate that has lost more than
0191 `hitShareThreshold` of its hits to better candidates is dropped.
0192 - **Seed splitting.** Short, central candidates are checked for self-consistency
0193 by fitting the circle through three different hit subsets. If the three
0194 curvature estimates disagree by more than `maxInvRadDiff`, the candidate is
0195 emitted as two shorter "drop-out" seeds instead of one.
0196
0197 The surviving candidates are written to the output @ref Acts::SeedContainer, with
0198 node indices translated back to the caller's own space point indices.
0199
0200 ## Machine-learning assisted acceptance {#gbts-ml}
0201
0202 When `useClusterWidthCuts` is enabled, GBTS narrows the per-node @f$\tau@f$
0203 window using a pre-trained lookup table indexed by **pixel cluster width**. The
0204 cluster a track leaves in a pixel module grows with the incidence angle, so the
0205 width alone constrains @f$\cot\theta@f$ before any pairing is attempted.
0206
0207 The table carries two sets of bounds per width bin: one for clusters comfortably
0208 inside the module, and one for clusters within `moduleEdgeTolerance` of the module
0209 edge, where the cluster may be truncated and the width therefore underestimates
0210 the angle. Wide clusters in the pixel endcap are dropped entirely
0211 (`maxEndcapClusterWidth`).
0212
0213 > [!note]
0214 > The seeder takes the table itself as `tauLookupTable`, not a path to it;
0215 > `ActsExamples::GraphBasedSeedingAlgorithm` parses it from ATLAS' text format.
0216 > It is only consulted for pixel barrel layers, and the ACTS examples framework
0217 > does not currently provide cluster widths or local positions, so this path is
0218 > exercised only by experiment-side integrations that supply them through
0219 > `insert`.
0220
0221 ## Configuration {#gbts-configuration}
0222
0223 The main knobs on @ref Acts::Experimental::GraphBasedTrackSeeder "GraphBasedTrackSeeder::Config":
0224
0225 | Option | Stage | Effect |
0226 | --- | --- | --- |
0227 | `useStripConnections` | @ref gbts-geometry | take the strip layer connections from the connector file instead of the pixel ones |
0228 | `minPt` | @ref gbts-graph | drives the curvature and @f$\phi@f$-window bounds |
0229 | `nMaxPhiSlice` | @ref gbts-graph | sets the base @f$\phi@f$ sliding-window width |
0230 | `useOldTuningsCurvature`, `useOldTuningsPhiWindow` | @ref gbts-graph | bound the curvature and the @f$\phi@f$ window by the pT the triplet has to reach, rather than by the tuned constants |
0231 | `minDeltaRadius`, `maxAbsTau` | @ref gbts-graph | doublet acceptance |
0232 | `minZ0`, `maxZ0`, `doubletFilterRZ` | @ref gbts-graph | luminous-region cuts on the doublet |
0233 | `tauRatioCut`, `cutDPhiMax`, `cutDCurvMax` | @ref gbts-graph | edge-to-edge linking tolerances |
0234 | `useAdaptiveCuts`, `tauRatioCorr` | @ref gbts-graph | widen the @f$\tau@f$ tolerance when a layer is skipped |
0235 | `validateTriplets`, `d0Max` | @ref gbts-graph | circle fit on pixel-barrel triplets |
0236 | `nMaxEdges` | @ref gbts-graph | hard cap on the edge array (2M by default); exceeding it costs efficiency |
0237 | `matchBeforeCreate`, `tauRatioPrecut` | @ref gbts-graph | require a compatible incoming edge before creating one |
0238 | `hitShareThreshold` | @ref gbts-extraction | fraction of shared hits above which a candidate is a clone |
0239 | `maxSeedSplitEta`, `maxInvRadDiff` | @ref gbts-extraction | seed splitting |
0240 | `addTriplets`, `maxAbsEtaAddTriplets` | @ref gbts-extraction | allow shorter chains within an @f$\eta@f$ range |
0241 | `useClusterWidthCuts`, `tauLookupTable` | @ref gbts-ml | cluster-width based @f$\tau@f$ windows |
0242 | `maxEndcapClusterWidth`, `moduleHalfLengthY`, `moduleEdgeTolerance` | @ref gbts-ml | cluster-width acceptance and module-edge handling |
0243
0244 There is no large radius tracking mode. LRT is these options set to the values
0245 it needs: `useStripConnections`, `useOldTuningsCurvature` with both
0246 `oldTuningsCurvature*Fraction` at 1, and `minSeedLevel = 2`.
0247
0248 @ref Acts::Experimental::GbtsTrackingFilter "GbtsTrackingFilter::Config"
0249 separately controls the chain-following filter of @ref gbts-extraction "seed extraction":
0250
0251 | Option | Effect |
0252 | --- | --- |
0253 | `sigmaX`, `sigmaY` | measurement resolution in the bending plane and along @f$z@f$ |
0254 | `maxDChi2X`, `maxDChi2Y` | per-step @f$\chi^2@f$ ceilings; a branch exceeding either is dropped |
0255 | `addHit`, `weightX`, `weightY` | the reward and the two @f$\chi^2@f$ weights in the branch score |
0256 | `sigmaMS`, `radLen` | multiple-scattering inflation added before each extrapolation |
0257 | `maxCurvature`, `maxZ0` | track-level bounds checked after each update |
0258
0259 ## Implementation pointers {#gbts-implementation}
0260
0261 - Seeder and configuration: @ref Acts::Experimental::GraphBasedTrackSeeder.
0262 - Node storage: @ref Acts::Experimental::GbtsNodeStorage. The graph EDM it
0263 holds - `GbtsNodeParams`, `GbtsNodeEdgeInfo`, `GbtsEtaBinInfo`, `GbtsEdge` -
0264 is internal and lives in `Acts/Seeding/detail/GbtsGraphTypes.hpp`.
0265 - Geometry: @ref Acts::Experimental::GbtsGeometry,
0266 @ref Acts::Experimental::GbtsLayerConnection, and the internal `GbtsLayer`.
0267 - Chain following: @ref Acts::Experimental::GbtsTrackingFilter and its internal
0268 `GbtsEdgeState`.
0269 - Region of interest: @ref Acts::Experimental::GbtsRoiDescriptor.
0270 - Connection-table training: @ref Acts::Experimental::GbtsLayerConnectionTool.
0271 - Examples integration: `ActsExamples::GraphBasedSeedingAlgorithm`, driven from
0272 `Examples/Scripts/Python/full_chain_itk_Gbts.py`.
0273
0274 A GPU implementation of the same algorithm, using an equivalent
0275 struct-of-arrays layout, lives in the traccc plugin under
0276 `Traccc/device/common/include/traccc/gbts_seeding`.