Warning, /snippets/Tracking/svt_rate_per_source/README.md is written in an unsupported language. File is not indexed.
0001 # SVT reconstructed-hit rates by source
0002 Original implemented by Shujie Li, Oct 2026.
0003 Refactored by Chatgpt.
0004
0005 Read EICrecon `events` trees, assign each SVT reconstructed hit to its MC source,
0006 and produce surface maps and rate tables. Copy this entire folder anywhere;
0007 it needs no files or imports from the parent analysis project, ROOT bindings,
0008 PODIO bindings, or detector installation. Input data and Python packages are
0009 the only external requirements.
0010
0011 | File | Purpose |
0012 | --- | --- |
0013 | `plot_hit_rate_per_source.py` | Command-line workflow: chunked input → counts → rates → PDFs/CSV. |
0014 | `svt_sources.py` | Explicit SVT windows, relation names, source mapping, and ancestry tracing. |
0015 | `requirements.txt` | Four required Python packages; Python 3.9 or newer. |
0016 | `test_svt_rate.py` | Offline numerical, relation, chunking, and output checks. |
0017
0018 ## Start here
0019
0020 Use an existing analysis environment, or create one from this folder:
0021
0022 ```bash
0023 python3 -m venv .venv
0024 source .venv/bin/activate
0025 python -m pip install -r requirements.txt
0026 # Only for root:// inputs; omit when using local files:
0027 python -m pip install XRootD
0028 python plot_hit_rate_per_source.py --help
0029 ```
0030
0031 A short check on a local file, followed by a full run:
0032
0033 ```bash
0034 python -u plot_hit_rate_per_source.py \
0035 --input-files /path/to/sample.eicrecon.edm4eic.root \
0036 --label check --entry-stop 10 --chunk-size 5
0037
0038 python -u plot_hit_rate_per_source.py \
0039 --input-files /path/to/sample.eicrecon.edm4eic.root \
0040 --label full --chunk-size 50
0041 ```
0042
0043 Outputs default to `outputs/LABEL/` beside the script. Use a new label for
0044 each run; nonempty output folders are protected from overwriting. Save your
0045 production command in a shell script before running it.
0046
0047 ## Several files or remote input
0048
0049 Pass several paths after `--input-files`, or use `--file-list inputs.txt`.
0050 The text list contains one local path or full URL per line; blank lines and
0051 lines beginning with `#` are ignored. Local paths in a list are relative to
0052 the list's directory; command-line paths are relative to your working directory.
0053 Both forms accept an optional `:events` suffix. Duplicate inputs are rejected.
0054
0055 JLab's current XRootD layout is:
0056
0057 ```text
0058 root://dtn2304.jlab.org:8443//jlab-osdf-ro/eic/EPIC/volatile/RECO/<campaign>/<dataset>/<file>.root
0059 ```
0060
0061 Use an actual file URL from the [EIC production file catalog/access guide](https://eic.github.io/software/production_file.html);
0062 availability depends on the campaign. BNL and other full URLs are accepted
0063 unchanged. This release opens exactly the supplied locations; it does not
0064 switch servers automatically.
0065
0066 ```bash
0067 python -u plot_hit_rate_per_source.py \
0068 --file-list inputs.txt --label production --chunk-size 50
0069
0070 # Same inputs and normalization, finer spatial bins:
0071 python -u plot_hit_rate_per_source.py \
0072 --file-list inputs.txt --label production_tile --chunk-size 50 --tile
0073 ```
0074
0075 | Option | Interpretation |
0076 | --- | --- |
0077 | `--entry-start A --entry-stop B` | Process `[A, B)` separately in every file; stop is clipped to its length. |
0078 | `--max-files N` | First N listed files; convenient for a quick check. |
0079 | `--chunk-size N` | Memory/IO control only; default 10 events. Reduce for busy background frames. |
0080 | `--frame-us T` | Duration of each equal-weight frame; default 2 µs. Controls rate normalization. |
0081 | `--tile` | 3.5 × 9.8 mm bins; default is 20 × 20 mm, called RSU here. |
0082 | `--min-surface-hits N` | Omit sparse PDF pages; preserves all counts and CSV rows. Default 1. |
0083 | `--output-dir DIR` | Write under `DIR/LABEL/`. |
0084
0085 ## Physics conventions — read before interpreting rates
0086
0087 Each selected event must represent the same physical duration and have equal
0088 statistical weight. Zero-hit frames count in the denominator. Files are pooled
0089 by event count, not averaged with equal weight per file.
0090
0091 ```text
0092 N = total number of selected event frames across all files
0093 T = duration per frame in µs
0094 rate_scale = 1000 / (N × T) [kHz per counted hit]
0095 bin_rate = accumulated hits in bin × rate_scale
0096 surface_rate = sum of in-map bin rates
0097 peak_bin_rate = maximum of the accumulated bin rates
0098 ```
0099
0100 At T = 2 µs, this reproduces the original `500/N` scale. It estimates hit
0101 arrival rates, not the probability of occupancy, event rate, or a rate per area.
0102 Single-particle or triggered/weighted DIS samples need an appropriate physical
0103 normalization; the default 2 µs assumption does not establish one.
0104
0105 The source path is `RecHit → RawHit → RawHitAssociation → SimHit → MCParticle`.
0106 For multiple associations, choose the largest weight; equal weights choose
0107 the first row. For `generatorStatus == 0`, repeatedly follow the first MC
0108 parent to a nonzero status. Cycles, missing parents, invalid parent ranges,
0109 and chains beyond 100 links remain unresolved and enter `other`. ObjectID
0110 indices are event-local within the explicitly named collections; collection
0111 IDs are not remapped to alternative detector collections.
0112
0113 | MC generatorStatus | Source label |
0114 | --- | --- |
0115 | 1, 2 | DIS |
0116 | 2001, 2002 | SR |
0117 | 3001, 3002 | Bremstrahlung (spelling retained from the parent workflow) |
0118 | 4001, 4002 | Coulomb |
0119 | 5001, 5002 | Touschek |
0120 | 6001, 6002 | Proton beam gas |
0121 | Other values, including unresolved 0 | other |
0122
0123 `unmatched` means a valid raw-hit index has no association. `invalid_relation`
0124 covers null rec-hit relations, invalid selected sim/particle indices, and
0125 nonfinite or negative weights. Diagnostics are always saved in `*_run.json`.
0126 `ALL` includes every in-map rec hit, including these categories.
0127 `other background` is the inclusive sum of the four non-SR beam backgrounds;
0128 do not add it or `ALL` to the exclusive sources when computing a total.
0129 The program checks exclusive-source conservation against `ALL` bin by bin.
0130
0131 Barrels use `(z, rφ)`, with `φ = atan2(y, x)` in radians and r in mm; disks
0132 use `(x, y)`. The five barrel and ten disk windows are listed in
0133 `svt_sources.py`; window endpoints are excluded. These fixed projected grids
0134 approximate RSU/tile dimensions, not the physical sensor segmentation or
0135 cellID layout. Bins are anchored at the negative map edge and have full nominal
0136 widths; the final bin may extend beyond the nominal map extent. Selected hits
0137 outside the grids appear in `outside_map_hits` and are excluded from rates.
0138 Update the windows when using a different geometry.
0139
0140 ## Outputs and reuse
0141
0142 | `LABEL_…` output | Contents |
0143 | --- | --- |
0144 | `SOURCE_svt_hit_rate.pdf` | One page per populated surface; color is bin rate in kHz. |
0145 | `summary.csv` | Long table by `lname`, `source`, and `rec_branch`; rates, raw counts, occupied-frame counts, and out-of-map counts. |
0146 | `input_files.csv` | Exact URLs/paths, available events, actual `[start, stop)` ranges, and processed counts, including skipped files. |
0147 | `maps.npz` | Integer raw histograms and bin edges in mm; axes are `(x, y)` as defined above. |
0148 | `run.json` | Normalization, arguments, versions, code hashes, windows, relations, diagnostics, and archive-key index. |
0149
0150 `rsu_or_tile_max_rate_khz` is the peak of the time-averaged map;
0151 `max_raw_hits_per_event_tile` is the largest bin count in any single frame
0152 (the historical column name is retained for RSU runs too).
0153 Absent source/surface rows mean no hits were accumulated for that source.
0154
0155 CSV headers beginning with `#` contain normalization metadata. Example:
0156
0157 ```python
0158 import pandas as pd # Optional; not required to run this release.
0159 summary = pd.read_csv("outputs/full/full_summary.csv", comment="#")
0160 ```
0161
0162 Recover a rate map without rereading ROOT:
0163
0164 ```python
0165 import json
0166 import numpy as np
0167 run = json.load(open("outputs/full/full_run.json"))
0168 with np.load("outputs/full/full_maps.npz") as maps:
0169 entry = next(m for m in run["maps"] if m["lname"] == "L0" and m["source"] == "ALL")
0170 rate_khz = maps[entry["key"]] * run["rate_scale_khz_per_count"]
0171 xedges_mm = maps[entry["xedges_key"]]
0172 yedges_mm = maps[entry["yedges_key"]]
0173 ```
0174