Warning, /acts/docs/groups/detector_descr/surface_array.md is written in an unsupported language. File is not indexed.
0001 @defgroup surface_array Surface array
0002 @ingroup geometry
0003 @brief Binned lookup from a position on a layer to the sensitive surfaces near it
0004
0005 @ref Acts::SurfaceArray answers one question: given where a track crosses a
0006 layer, which sensitive surfaces could it hit. It holds the surfaces of one
0007 layer in a two-dimensional grid drawn on a representative surface and returns,
0008 for a position and a direction, the surfaces registered in the bins around the
0009 crossing point.
0010
0011 The figures for the two things that decide whether that works - which bins hold
0012 a surface, and how far from the crossing bin the module it hits can be - are on
0013 a separate, interactive page:
0014 [filling and lookup, in figures](surface_array_figures.html). Everything below
0015 is the written description; the page carries the pictures and the measurements.
0016
0017 ## Overview
0018
0019 A layer holds its sensitive surfaces in a `SurfaceArray`, and navigation asks
0020 the array instead of testing every module:
0021
0022 - @ref Acts::SurfaceArrayCreator builds it, through
0023 @ref Acts::SurfaceArrayCreator::surfaceArrayOnCylinder,
0024 @ref Acts::SurfaceArrayCreator::surfaceArrayOnDisc or
0025 @ref Acts::SurfaceArrayCreator::surfaceArrayOnPlane, usually called from
0026 @ref Acts::LayerCreator.
0027 - @ref Acts::Layer owns it and queries it from
0028 @ref Acts::Layer::compatibleSurfaces. This is the Gen1 navigation path.
0029 - @ref Acts::SurfaceArrayNavigationPolicy builds one for a Gen3 layer volume
0030 and feeds the result into the navigation stream.
0031
0032 Both consumers call @ref Acts::SurfaceArray::neighbors with the geometry
0033 context, the position and the direction, and receive a `std::span` of surface
0034 pointers. The array does not intersect the surfaces it returns. It narrows the
0035 candidates; the caller intersects them.
0036
0037 The single-surface constructor builds an array without a grid. Every lookup
0038 returns that one surface.
0039
0040 ## The grid
0041
0042 The grid is drawn on a representative @ref Acts::RegularSurface that the
0043 creator derives from the extent of the surfaces:
0044
0045 | Layer | Representative surface | Axis 0 | Axis 1 |
0046 |----------|----------------------------|---------------|---------------|
0047 | Cylinder | @ref Acts::CylinderSurface | phi, `Closed` | z, `Bound` |
0048 | Disc | @ref Acts::DiscSurface | r, `Bound` | phi, `Closed` |
0049 | Plane | @ref Acts::PlaneSurface | first in-plane direction, `Bound` | second in-plane direction, `Bound` |
0050
0051 The axes are @ref Acts::IAxis objects, equidistant or variable, and every
0052 lookup implementation is templated on both axis types behind the
0053 `ISurfaceGridLookup` interface. The boundary type (@ref Acts::AxisBoundaryType)
0054 fixes what happens at the edge. A `Closed` axis wraps: the bin after the last
0055 phi bin is the first, and a neighborhood of an edge bin continues on the other
0056 side. A `Bound` axis clamps: a position past the edge lands in the edge bin and
0057 a neighborhood stops there. Each axis also carries an underflow and an
0058 overflow bin, so @ref Acts::SurfaceArray::size returns `(n0 + 2) * (n1 + 2)`.
0059
0060 Bin edges are expressed in the local frame of the representative surface. On a
0061 cylinder and a disc the creator rotates that frame so that the phi seam at
0062 +-pi falls between modules, never on a module center.
0063
0064 ### Surface local versus grid local
0065
0066 The grid is binned in the quantities the axes name. The bound-local
0067 coordinates of the representative surface are not always those: a
0068 @ref Acts::CylinderSurface measures arc length `R * phi` along its first local
0069 coordinate while the grid bins `phi`. `surfaceToGridLocal` and
0070 `gridToSurfaceLocal` divide and multiply by the cylinder radius; on a disc and
0071 a plane they are the identity. Both maps are linear, so they carry a
0072 displacement the same way as a position.
0073
0074 ## Filling
0075
0076 The array registers every surface in every bin its projection onto the
0077 representative surface overlaps. `fillSurfaceFootprint` does this once per
0078 surface at construction:
0079
0080 1. Confirm that the surface belongs to the layer: its reference position must
0081 project onto the representative surface within the layer tolerance.
0082 2. Take the outline from @ref Acts::Surface::polyhedronRepresentation and walk
0083 each edge in 32 samples. A straight edge in space is not straight in grid
0084 coordinates, so the vertices alone are not enough.
0085 3. Project each sample along the normal of the representative surface onto
0086 it, convert to grid coordinates and to a bin. Samples in the underflow or
0087 overflow bins are dropped.
0088 4. Group the sampled bins into columns keyed by the closed axis. Per column
0089 keep the lowest and highest bin along the other axis, and register the
0090 surface in every bin of every column span.
0091
0092 The footprint is sampled, so sufficiently fine bins can be missed between
0093 samples. Filling the span can also include extra cells for a concave outline.
0094 Keying the columns by the axis that wraps prevents a span across the phi seam:
0095 a module straddling +-pi produces columns at both ends, each with its own span
0096 along the bound axis.
0097
0098 The optional `overfill` argument of @ref Acts::SurfaceArray,
0099 @ref Acts::SurfaceArrayCreator and @ref Acts::LayerCreator expands every matched
0100 cell during construction. It defaults to zero. A value of one also fills the
0101 immediate neighbors, including diagonals; two reaches the next neighbors, and
0102 `n` reaches all cells within `n` bin steps on each axis. Closed axes wrap, and
0103 expansion stops at nonperiodic grid edges. Underflow and overflow cells remain
0104 empty. Overlapping contributions are deduplicated.
0105
0106 Overfilling changes the stored contents returned by `at()` as well as
0107 `neighbors()`. It is independent of `NeighborWindow`: the latter gathers those
0108 contents around the lookup cell. Overfilling can provide a margin for alignment
0109 or sampling gaps, at the cost of more candidates; it does not guarantee that a
0110 sampled footprint covers arbitrarily fine grids.
0111
0112 ```cpp
0113 SurfaceArray array(gctx, surfaces, representative, tolerance, axes,
0114 SurfaceArray::NeighborWindow{{0, 0}, {2, 2}},
0115 /*overfill=*/2);
0116 ```
0117
0118 [A module projected onto a disc grid](surface_array_figures.html#fill), the two
0119 point tests the footprint fill replaced, and what each of them costs in bins are
0120 in the figures.
0121
0122 After all surfaces are filled the bin contents are sorted and deduplicated,
0123 `checkGrid` verifies that every input surface landed in at least one bin, and
0124 the neighbor cache is built. The filling grid is scratch; its contents
0125 survive as the zero-distance packs of the cache.
0126
0127 ## Lookup
0128
0129 @ref Acts::SurfaceArray::neighbors "neighbors(gctx, position, direction)" is
0130 the navigation entry point:
0131
0132 1. Intersect the representative surface along `direction` from `position`
0133 (`findCrossing`). No intersection means an empty result.
0134 2. Convert the crossing to grid coordinates and to a bin.
0135 3. Compute the neighbor distance per axis (`crossingNeighborDistance`).
0136 4. Return the cached pack for that bin and distance.
0137
0138 @ref Acts::SurfaceArray::at "at(gctx, position, direction)" skips step 3 and
0139 returns the content of the crossing bin alone.
0140
0141 ### Why a window
0142
0143 The lookup happens where the track crosses the representative surface; a module
0144 is registered where it projects onto that surface. Between the two the track
0145 moves along the layer, the further the shallower the crossing, and the window
0146 around the crossing bin spans that movement.
0147
0148 `crossingNeighborDistance` derives the slide from the crossing geometry:
0149
0150 - The array is built with a `tolerance`, the half thickness of the layer. A
0151 track with direction `d` meeting the representative surface with normal `n`
0152 travels `tolerance / |n . d|` inside the layer on each side of the surface.
0153 - Dropping the component along `n` leaves the part of that path that runs
0154 along the layer: `slide = (tolerance / |n . d|) * (d - (n . d) n)`.
0155 - The slide is a global displacement.
0156 @ref Acts::Surface::localCartesianToBoundLocalDerivative maps it into the
0157 bound-local frame of the representative surface with the curvilinear scaling
0158 included: `(d(R phi), dz)` on a cylinder, `(dr, dphi)` on a disc,
0159 `(dx, dy)` on a plane. `surfaceToGridLocal` turns that into a step in grid
0160 coordinates.
0161 - The bins at `+slide` and `-slide` from the crossing point give the distance
0162 per axis, in bins, following the signed displacement across a closed-axis
0163 seam. A full turn covers the axis rather than wrapping the distance to zero.
0164 The larger of the two sides is kept.
0165
0166 The derivative supplies the local metric. This is a first-order estimate on
0167 curved surfaces, bounded by the configured window. At a coordinate singularity,
0168 an undefined displacement opens the affected axis to its configured bound.
0169
0170 [The same crossing, in r-z and on the bin
0171 grid](surface_array_figures.html#window), and the miss rate each window policy
0172 carries across the barrel, are in the figures.
0173
0174 Below an incidence `|n . d|` of `1e-4` the track runs along the layer and the
0175 window opens to its bound.
0176
0177 ## The neighbor window
0178
0179 @ref Acts::SurfaceArray::NeighborWindow bounds the distance per axis:
0180
0181 ```cpp
0182 struct NeighborWindow {
0183 std::array<std::uint8_t, 2> min = {0, 0};
0184 std::array<std::uint8_t, 2> max = {2, 2};
0185 };
0186 ```
0187
0188 The computed distance is clamped into `[min, max]` on each axis. `max` also
0189 sizes the neighbor cache, so it is a cost as well as a bound. The defaults set
0190 by @ref Acts::SurfaceArrayCreator and @ref Acts::LayerCreator are:
0191
0192 | Layer | `max` | Meaning |
0193 |----------|----------|------------------------------|
0194 | Cylinder | `{1, 2}` | one bin in phi, two in z |
0195 | Disc | `{2, 1}` | two bins in r, one in phi |
0196 | Plane | `{2, 2}` | two bins in either direction |
0197
0198 `min` is `{0, 0}` everywhere. The floor exists for lookups under a geometry
0199 context the fill did not see. The fill runs once, in the construction context;
0200 a lookup under an alignment context sees moved surfaces, and a floor of one bin
0201 keeps them reachable. `{1, 1}` for both `min` and `max` reproduces the fixed
0202 3x3 lookup the array used before the window was sized by the crossing angle.
0203 When `min` equals `max` the angle is not evaluated.
0204
0205 Constructing a `NeighborWindow` from a single `std::uint8_t` is deprecated; it
0206 sets `max` to that value on both axes and `min` to one, or to zero if the bound
0207 is zero.
0208 @ref Acts::SurfaceArray::maxNeighborDistance is deprecated in favor of
0209 @ref Acts::SurfaceArray::neighborWindow.
0210
0211 ## The neighbor cache
0212
0213 The set of surfaces reachable from a bin at a given distance is fixed once the
0214 array is filled. `populateNeighborCache` precomputes it for every valid bin and
0215 every `(distance0, distance1)` up to `max`. Each result is a sorted,
0216 deduplicated pack of surface pointers. Identical packs are stored once, found
0217 through a hash table that reads packs back through their offsets, and the index
0218 array holds `(offset, count)` pairs of 32-bit integers.
0219
0220 A lookup is therefore one index computation,
0221 `bin * stridePerBin + distance0 * stride1 + distance1`, and a span into the
0222 pack storage. No gather over neighboring bins happens at lookup time.
0223
0224 The index array has `(n0 + 2) * (n1 + 2) * (max0 + 1) * (max1 + 1)` entries.
0225 Both this index and the pack contents cost memory: each pack lists the
0226 surfaces of up to `(2 max0 + 1) * (2 max1 + 1)` bins. On the generic detector
0227 at the default bounds the cache is 13.6 MB. A lookup on a toy barrel costs
0228 about 80 ns; [what that is made of](surface_array_figures.html#cost) is in the
0229 figures.
0230
0231 `SurfaceArray` is also used by @ref Acts::SurfaceArrayNavigationPolicy in Gen3
0232 geometry.