Back to home page

EIC code displayed by LXR

 
 

    


Warning, /EICrecon/docs/design/svt_pixel_noise_injection.md is written in an unsupported language. File is not indexed.

0001 # RandomNoisePixel: silicon noise injection explained
0002 
0003 ## 1. What problem does this algorithm solve?
0004 
0005 A silicon tracker contains many electronic pixels. Even when no charged particle crosses a pixel,
0006 electronics noise can occasionally make that pixel appear to fire. `RandomNoisePixel` adds these
0007 noise-only hits to an EICrecon event.
0008 
0009 The model starts from one probability:
0010 
0011 ```text
0012 p = noiseRate
0013 ```
0014 
0015 The default is `p = 2e-7` per pixel per event. In other words, a particular pixel has a probability
0016 of approximately two in ten million of producing a noise hit in one event. The same `p` is used
0017 throughout the SVT. Layers with more pixels have more expected noise hits.
0018 
0019 The current implementation supports:
0020 
0021 | Detector system | Segmentation coordinates | Sensor treatment |
0022 | --- | --- | --- |
0023 | BVTX | cylindrical phi-z | rectangular phi and z index ranges |
0024 | BTRK | Cartesian x-y | rectangular x and y index ranges |
0025 | ECTRK | Cartesian x-z | row-by-row trapezoid ranges |
0026 
0027 The implementation is in
0028 [`RandomNoisePixel.cc`](../EICrecon/src/algorithms/digi/RandomNoisePixel.cc), with data structures in
0029 [`RandomNoisePixel.h`](../EICrecon/src/algorithms/digi/RandomNoisePixel.h) and user parameters in
0030 [`RandomNoisePixelConfig.h`](../EICrecon/src/algorithms/digi/RandomNoisePixelConfig.h).
0031 
0032 ## 2. The statistical model
0033 
0034 Let:
0035 
0036 - `p` be the noise probability per pixel per event;
0037 - `N_m` be the number of addressable pixels on sensitive component `m`;
0038 - `N_l = sum_m N_m` be the total number of pixels in layer `l`.
0039 
0040 The expected number of noise hits in a layer is
0041 
0042 ```text
0043 lambda_l = p * N_l.
0044 ```
0045 
0046 The event-by-event hit count is drawn from
0047 
0048 ```text
0049 K_l ~ Poisson(lambda_l).
0050 ```
0051 
0052 The exact independent-pixel model would be `Binomial(N_l, p)`. The Poisson model is an excellent
0053 approximation because `p` is very small and `N_l` is very large. It also avoids a random trial for
0054 every pixel.
0055 
0056 ### Worked rate example
0057 
0058 Suppose one layer has two sensitive components:
0059 
0060 ```text
0061 component A: N_A = 1,000,000 pixels
0062 component B: N_B = 3,000,000 pixels
0063 ```
0064 
0065 For `p = 2e-7`,
0066 
0067 ```text
0068 N_l      = 4,000,000 pixels
0069 lambda_l = 2e-7 * 4,000,000 = 0.8 noise hits/event.
0070 ```
0071 
0072 The event may contain zero, one, two, or more noise hits; `0.8` is the long-run average. Component A
0073 is selected with probability `1/4`, and component B with probability `3/4`. Every individual pixel
0074 therefore has the same probability of being selected.
0075 
0076 ## 3. The two phases
0077 
0078 `RandomNoisePixel` separates geometry work from event work:
0079 
0080 ```text
0081 DD4hep geometry
0082       |
0083       v
0084 init(): discover sensors and build compact pixel layouts
0085       |
0086       v
0087 cached IDs, pixel ranges, and layer totals
0088       |
0089       v
0090 process(): draw noise counts and create hits for each event
0091 ```
0092 
0093 Geometry navigation is relatively expensive and involves shared TGeo state. It is done once in
0094 `init()`. Event processing uses cached integer information and does not navigate TGeo.
0095 
0096 ## 4. Initialization: understand the detector once
0097 
0098 ### Step 1: validate the configuration
0099 
0100 If `addNoise` is false, initialization stops without building a cache. Otherwise, the configured
0101 rate must satisfy `0 <= p <= 1`, and the requested DD4hep readout must exist. Each factory instance
0102 uses one readout, such as `VertexBarrelHits`, `SiBarrelHits`, or `TrackerEndcapHits`.
0103 
0104 ### Step 2: find sensitive silicon components
0105 
0106 The code traverses the DD4hep detector hierarchy and keeps only sensitive placements whose readout
0107 matches the factory configuration. A module may itself be sensitive, or it may contain sensitive
0108 daughter volumes. The traversal handles both cases.
0109 
0110 For every sensitive placement, initialization temporarily records:
0111 
0112 - detector name and layer number;
0113 - its `TGeoVolume` and shape;
0114 - its local-to-world `TGeoHMatrix` transformation.
0115 
0116 The transform identifies the placed sensor and validates the final cell position. It is discarded
0117 after initialization, so it consumes no persistent event-time cache memory.
0118 
0119 The transform includes two distinct steps:
0120 
0121 ```text
0122 sensitive-solid local point
0123   -> add/rotate by the component placement inside its module
0124   -> apply the module and all ancestor placements to reach world coordinates
0125 ```
0126 
0127 This distinction matters when, for example, left and right silicon pieces are offset from the module
0128 center. The code composes the matrices once; it neither drops the component offset nor adds it a
0129 second time.
0130 
0131 ### Step 3: determine the sensor's base volume ID
0132 
0133 A cell ID contains two kinds of information:
0134 
0135 ```text
0136 placement fields: detector, layer, module, sensor, ...
0137 pixel fields:     x/y, x/z, or phi/z indices
0138 ```
0139 
0140 The code chooses a point inside the sensor, transforms it from local to global coordinates, and asks
0141 `CellIDPositionConverter` for its complete cell ID. The segmentation's `volumeID()` operation
0142 removes the local pixel fields, leaving `baseVolumeID`.
0143 
0144 During event processing, the algorithm starts from this base ID and writes new pixel indices into
0145 it. Packed numerical cell IDs are never assumed to form one continuous integer interval.
0146 
0147 ### Step 4: read the actual segmentation
0148 
0149 Pixel pitch and offset come directly from DD4hep. The algorithm has no separate 20 micrometre pitch
0150 parameter. If the segmentation changes, the cached pixel count changes automatically. The supplied
0151 per-pixel noise rate is not silently rescaled.
0152 
0153 For a `MultiSegmentation`, the placement fields select the correct concrete sub-segmentation before
0154 the pixel layout is calculated.
0155 
0156 ## 5. Compact representations of valid pixels
0157 
0158 The algorithm never stores a cell ID for every physical pixel. Instead, it stores index ranges.
0159 
0160 ### Rectangular Cartesian sensor
0161 
0162 For a rectangle, all pixel centers inside these limits are valid:
0163 
0164 ```text
0165 firstMin  <= firstIndex  <= firstMax
0166 secondMin <= secondIndex <= secondMax
0167 ```
0168 
0169 The pixel count is
0170 
0171 ```text
0172 N = (firstMax - firstMin + 1) * (secondMax - secondMin + 1).
0173 ```
0174 
0175 For example, x indices `2...5` and y indices `10...12` describe `4 * 3 = 12` pixels. Only the four
0176 limits and total count are stored.
0177 
0178 ### Trapezoidal sensor
0179 
0180 An ECTRK trapezoid does not fill its rectangular bounding box. Its valid x range changes with z.
0181 The code examines each candidate z row and checks pixel centers with `TGeoShape::Contains()`.
0182 
0183 For each non-empty row it stores:
0184 
0185 ```text
0186 z index    minimum x    maximum x    cumulative pixel total
0187 -------    ---------    ---------    ----------------------
0188    10         -1            1                  3
0189    11         -2            2                  8
0190    12         -3            3                 15
0191 ```
0192 
0193 The left and right boundaries are found with binary searches. This works for the current convex,
0194 centered trapezoids, where each row contains one continuous x interval.
0195 
0196 To select a pixel, draw one flat index from `0...14`. The cumulative totals identify its row. Flat
0197 index 6 belongs to the second row because `3 <= 6 < 8`. Its offset is `6 - 3 = 3`, giving
0198 
0199 ```text
0200 x = xMin + 3 = -2 + 3 = 1
0201 z = 11
0202 ```
0203 
0204 Every valid pixel has equal probability even though rows have different widths. Empty rows and
0205 invalid bounding-box corners are never sampled.
0206 
0207 ### Cylindrical phi-z sensor
0208 
0209 For `CylindricalGridPhiZ`, phi and z are coordinates in the sensitive volume's local cylindrical
0210 frame. The code obtains local bounds directly from the sensor shape and converts them into discrete
0211 indices. DD4hep applies the component's placement transform later when converting the cell ID into a
0212 global hit position; applying that transform while building the indices would place the sensor
0213 twice.
0214 
0215 Phi requires care near the `-pi`/`+pi` boundary. Angular differences are measured around the
0216 sensor's central phi so that a sensor crossing this boundary is not mistaken for one spanning almost
0217 the entire circle.
0218 
0219 ### Pixel-center convention
0220 
0221 A pixel is counted when its segmentation-defined center is inside the sensitive TGeo shape.
0222 Partial geometrical overlap at a sensor edge is not treated as a fractional pixel. This deterministic
0223 convention treats pixels as electronic channels.
0224 
0225 Initialization checks representative pixels at the beginning, quarters, middle, and end of every
0226 placed component's address range. Each cell ID is converted by DD4hep to a global center and then
0227 transformed back into that exact sensitive solid. The point must be inside the TGeo shape. A point
0228 on a face is accepted only within ROOT's geometry tolerance, which avoids false failures from
0229 floating-point round trips while still detecting incorrect offsets or rotations.
0230 
0231 ## 6. Sharing layouts between repeated sensors
0232 
0233 Many placed sensors have the same logical TGeo volume and Cartesian segmentation. Their pixel
0234 ranges are identical even though their placement IDs differ. These components share one immutable
0235 `PixelLayout` through a `shared_ptr`.
0236 
0237 Each placed component retains only:
0238 
0239 - detector name and layer;
0240 - `baseVolumeID`;
0241 - a shared pixel-layout pointer;
0242 - its pixel count.
0243 
0244 The full transform and TGeo volume pointer are initialization-only data. The cache therefore scales
0245 mainly with the number of components plus rows in unique trapezoid layouts, not with the total number
0246 of pixels.
0247 
0248 ## 7. Building layer selection tables
0249 
0250 Components are grouped by detector name and layer number. Each layer stores component indices and
0251 cumulative pixel totals. For component sizes
0252 
0253 ```text
0254 N_0 = 100, N_1 = 300, N_2 = 200,
0255 ```
0256 
0257 the cumulative table is `[100, 400, 600]`. A layer-wide random number in `0...599` selects:
0258 
0259 - component 0 for `0...99`;
0260 - component 1 for `100...399`;
0261 - component 2 for `400...599`.
0262 
0263 `std::upper_bound` finds the component in logarithmic time. The selection probability is
0264 automatically proportional to component pixel count.
0265 
0266 ## 8. Processing one event
0267 
0268 ### Step 1: create a reproducible random-number generator
0269 
0270 The run and event identity come from the required `EventHeader`. `UniqueIDGenSvc` combines this
0271 identity with the algorithm name to make a deterministic seed. There is no silent fallback seed.
0272 
0273 ### Step 2: draw one count per layer
0274 
0275 For each layer:
0276 
0277 ```text
0278 mean = p * layer.totalPixels
0279 requestedHits ~ Poisson(mean).
0280 ```
0281 
0282 This is much cheaper than visiting every sensor or pixel in every event.
0283 
0284 ### Step 3: select a component and pixel
0285 
0286 For each requested hit:
0287 
0288 1. Draw a uniform layer-wide pixel number.
0289 2. Use the cumulative table to select a component with probability `N_m/N_l`.
0290 3. Draw a uniform pixel index from that component's compact layout.
0291 4. Convert the flat pixel index into two segmentation indices.
0292 5. Copy `baseVolumeID` and set those fields with the DD4hep bit-field coder.
0293 
0294 The first draw chooses a correctly weighted component. The second chooses a uniform pixel inside
0295 that component. Together they give uniform sampling over all layer pixels.
0296 
0297 ### Step 4: reject duplicate pixels
0298 
0299 Hits are stored in a map keyed by cell ID. If the same pixel is selected twice in one event, the
0300 second selection is retried. At the default occupancy, duplicates are extremely rare.
0301 
0302 Retries are bounded so an unexpectedly high occupancy cannot create an infinite loop. A warning is
0303 emitted if the requested number of unique hits cannot be produced.
0304 
0305 ### Step 5: create `RawTrackerHit` objects
0306 
0307 Each accepted pixel becomes an `edm4eic::RawTrackerHit`. The current charge and timestamp are
0308 
0309 ```text
0310 charge    = 1.0e6
0311 timestamp = 0
0312 ```
0313 
0314 The map is iterated in increasing cell-ID order, giving deterministic output ordering.
0315 
0316 ## 9. Computational scaling
0317 
0318 Let `M` be the number of sensitive components, `R` the number of cached rows across unique
0319 non-rectangular layouts, `L` the number of layer groups, and `K` the generated noise-hit count.
0320 
0321 | Operation | Scaling |
0322 | --- | --- |
0323 | Geometry traversal during initialization | `O(M)` |
0324 | Rectangular layout construction | `O(1)` per unique layout |
0325 | Trapezoid layout construction | `O(R)` plus boundary searches |
0326 | Persistent cache | `O(M + R)` |
0327 | Event count draws | `O(L)` |
0328 | Event hit generation | `O(K log M_layer)` |
0329 
0330 There is no array proportional to the total number of pixels and no per-event loop over every
0331 component. This is why the method remains practical for 100,000 or more sensitive components.
0332 
0333 ## 10. Assumptions and limitations
0334 
0335 1. Detector geometry and alignment remain static after `init()`.
0336 2. Unsupported segmentation types raise an error instead of using a silent area approximation.
0337 3. The row algorithm assumes a convex, centered shape with at most one valid interval per row. A
0338    non-convex sensor or one with holes would require multiple spans per row.
0339 4. Pixel centers define active channels at boundaries.
0340 5. The algorithm models occupancy, not pulse-height or timing distributions.
0341 6. The Poisson approximation is intended for sparse occupancies such as `2e-7`.
0342 
0343 ## 11. Function map for reading the source
0344 
0345 Read the implementation in this order:
0346 
0347 | Function or structure | Purpose |
0348 | --- | --- |
0349 | `PixelLayout`, `PixelRow` | Represent valid segmentation indices compactly |
0350 | `init()` | Top-level cache construction |
0351 | `collectDetectorComponents()` | Enter detector layers |
0352 | `collectLayerComponents()` | Find modules and sensitive descendants |
0353 | `cachePixelLayouts()` | Determine base IDs and create layouts |
0354 | `makeCartesianLayout()` | Build rectangular or row-span Cartesian layouts |
0355 | `makeCylindricalLayout()` | Build local cylindrical phi-z layouts |
0356 | `buildLayers()` | Construct pixel-weighted component tables |
0357 | `pixelIndices()` | Map a flat pixel number to two segmentation indices |
0358 | `randomCellID()` | Encode a sampled pixel into a complete cell ID |
0359 | `addNoiseHitsForLayer()` | Draw the layer count and create unique hits |
0360 | `process()` | Event entry point and deterministic output |