Back to home page

EIC code displayed by LXR

 
 

    


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.