|
| 1 | +# AGENTS.md — EventNtuple Coding Agent Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +EventNtuple is a Mu2e (Fermilab) art/ROOT analysis ntuple package. It fills a flat |
| 6 | +ROOT ntuple from Mu2e reconstruction output (KalSeeds, CaloCluster, CRV, etc.) and |
| 7 | +provides a Python/ROOT analysis library (RooUtil) and distributed job runner (roodask). |
| 8 | + |
| 9 | +**Framework:** `art` (Fermilab) + `cetmodules` CMake layer + `fhiclcpp` configuration. |
| 10 | +**Languages:** C++17 (modules, helpers, structs), FHiCL (job config), Python 3.12+ (helpers/tools). |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## Build Commands |
| 15 | + |
| 16 | +Environment must be set up via Muse before building: |
| 17 | + |
| 18 | +```bash |
| 19 | +mu2einit |
| 20 | +muse setup # or: muse setup AnalysisMDC2020 / AnalysisMDC2025 |
| 21 | +muse build -j4 --mu2eCompactPrint |
| 22 | +``` |
| 23 | + |
| 24 | +Alternative Spack-based build: |
| 25 | + |
| 26 | +```bash |
| 27 | +spack develop event-ntuple@main |
| 28 | +spack concretize -f && spack install |
| 29 | +``` |
| 30 | + |
| 31 | +Run the ntuple maker: |
| 32 | + |
| 33 | +```bash |
| 34 | +mu2e -c EventNtuple/fcl/from_mcs-mockdata.fcl -S your-art-filelist.txt |
| 35 | +``` |
| 36 | + |
| 37 | +--- |
| 38 | + |
| 39 | +## Test / Validation Commands |
| 40 | + |
| 41 | +All validation scripts are in `validation/`. There is no unit test framework; tests |
| 42 | +run the full art job and compare histograms. |
| 43 | + |
| 44 | +```bash |
| 45 | +# Quick smoke test: runs from_mcs-mockdata.fcl, creates histograms |
| 46 | +bash validation/quick_test.sh |
| 47 | + |
| 48 | +# Test all FCL files for a campaign |
| 49 | +bash validation/test_fcls.sh MDC2020 |
| 50 | +bash validation/test_fcls.sh MDC2025 |
| 51 | +bash validation/test_fcls.sh Run1B |
| 52 | + |
| 53 | +# Test RooUtil library |
| 54 | +bash validation/test_rooutil.sh |
| 55 | + |
| 56 | +# Test all RooUtil example macros |
| 57 | +bash validation/test_rooutil_examples.sh |
| 58 | + |
| 59 | +# Test Python ntuplehelper |
| 60 | +python validation/ntuplehelper-test.py |
| 61 | + |
| 62 | +# Test roodask distributed runner |
| 63 | +bash validation/roodask_test.sh |
| 64 | +``` |
| 65 | + |
| 66 | +To run a **single FCL test** manually: |
| 67 | + |
| 68 | +```bash |
| 69 | +mu2e -c EventNtuple/fcl/<specific>.fcl -S filelist.txt -n 100 |
| 70 | +``` |
| 71 | + |
| 72 | +--- |
| 73 | + |
| 74 | +## FHiCL Configuration Structure |
| 75 | + |
| 76 | +### Branch / MC Data Configuration (Key Area for Improvement) |
| 77 | + |
| 78 | +Branch configuration is defined in `fcl/prolog.fcl`. Each track type is a FHiCL table: |
| 79 | + |
| 80 | +``` |
| 81 | +DeM : { |
| 82 | + input : "MergeKKDeM" # input KalSeedPtr collection tag |
| 83 | + branch : "dem" # output ROOT branch name |
| 84 | + options : { |
| 85 | + fillMC : true # toggle MC truth filling for this branch |
| 86 | + fillHits : true # toggle hit-level info |
| 87 | + genealogyDepth : -1 # MC genealogy depth (-1 = all) |
| 88 | + matchDepth : -1 # MC match depth (-1 = all) |
| 89 | + } |
| 90 | + trkQualTags : ["TrkQualDeM"] |
| 91 | + trkPIDTags : ["TrkPIDDeM"] |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +Global MC switch lives in `EventNtupleMaker` config: `FillMCInfo : true/false`. |
| 96 | +Per-branch MC is controlled by `options.fillMC`. Both must be true for MC to fill. |
| 97 | + |
| 98 | +**Current issue:** Branch on/off and MC on/off are scattered across individual track |
| 99 | +tables in `prolog.fcl` and duplicated in every campaign FCL. When adding a new |
| 100 | +track type or toggling MC for a subset of branches, edits are required in many places. |
| 101 | + |
| 102 | +**Preferred direction for improvement:** |
| 103 | +- Define a single `BranchOptions` prolog table with canonical defaults. |
| 104 | +- Use `@table::` inheritance for per-branch overrides rather than full re-specification. |
| 105 | +- Consider a top-level `MCOptions` table that can be overlaid to flip all `fillMC` flags. |
| 106 | +- Campaign FCLs should `#include prolog.fcl` and only override what differs. |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +## C++ Code Style |
| 111 | + |
| 112 | +### File Naming |
| 113 | +- Headers: `PascalCase.hh` in `inc/` |
| 114 | +- Sources: `PascalCase.cc` in `src/` |
| 115 | +- Module plugins: `PascalCase_module.cc` |
| 116 | + |
| 117 | +### Namespaces |
| 118 | +All production code lives in `namespace mu2e {}`. |
| 119 | +RooUtil library uses `namespace rooutil {}`. |
| 120 | + |
| 121 | +### Struct / Class Naming |
| 122 | +- Ntuple info structs: `PascalCase` ending in `Info` or `InfoMC` (e.g. `TrkInfo`, `CaloClusterInfoMC`) |
| 123 | +- Each struct lives in its own `inc/<StructName>.hh` header |
| 124 | + |
| 125 | +### Member Naming |
| 126 | +- Module private members: leading underscore (`_conf`, `_fillmc`, `_ntuple`) |
| 127 | +- Struct data members: `snake_case` (no underscore suffix); initialized to sentinel values |
| 128 | + - Integers: `int status = -1;` |
| 129 | + - Floats: `float chisq = -1;` or `float mom = std::numeric_limits<float>::min();` |
| 130 | + - Booleans: `bool flag = false;` |
| 131 | + |
| 132 | +### Info Struct Pattern (required for `ntuplehelper` compatibility) |
| 133 | +```cpp |
| 134 | +namespace mu2e { |
| 135 | + struct FooInfo { |
| 136 | + // required comment on every leaf for ntuplehelper autodoc |
| 137 | + int status = -1; // fit status |
| 138 | + float mom = -1; // momentum at tracker entrance (MeV/c) |
| 139 | + |
| 140 | + void reset() { *this = FooInfo(); } |
| 141 | + }; |
| 142 | +} |
| 143 | +``` |
| 144 | +Every leaf **must** have an inline `//` comment — this is parsed by `ntuplehelper --list-all-branches`. |
| 145 | + |
| 146 | +### Includes (C++ ordering) |
| 147 | +1. Mu2e Offline headers (`Offline/…`) |
| 148 | +2. art framework headers (`art/…`, `canvas/…`, `fhiclcpp/…`) |
| 149 | +3. ROOT headers |
| 150 | +4. Local EventNtuple headers (`EventNtuple/inc/…`) |
| 151 | +5. C++ standard library (`<vector>`, `<string>`, etc.) |
| 152 | + |
| 153 | +### Error Handling |
| 154 | +- Throw via `cet::exception`: `throw cet::exception("EventNtuple") << "message";` |
| 155 | +- Do not use raw `std::exception` or `exit()` in art modules |
| 156 | +- Warnings via `mf::LogWarning("EventNtuple") << "message";` |
| 157 | + |
| 158 | +### FHiCL Config Structs (in modules) |
| 159 | +```cpp |
| 160 | +struct Config { |
| 161 | + fhicl::Atom<bool> fillMC { Name("FillMCInfo"), Comment("Fill MC info"), true }; |
| 162 | + fhicl::Table<SubConf> sub { Name("SubConfig") }; |
| 163 | + fhicl::Sequence<art::InputTag> tags { Name("InputTags") }; |
| 164 | +}; |
| 165 | +``` |
| 166 | +
|
| 167 | +--- |
| 168 | +
|
| 169 | +## Python Code Style |
| 170 | +
|
| 171 | +- Python 3.12+; no type annotations required but welcome |
| 172 | +- `snake_case` for all functions, methods, and variables |
| 173 | +- Class names: `PascalCase` |
| 174 | +- No f-strings required; format strings acceptable |
| 175 | +- `roodask/roodask.py` is intentionally a **single-file script** — do not split it |
| 176 | +
|
| 177 | +--- |
| 178 | +
|
| 179 | +## FHiCL Style Guidelines |
| 180 | +
|
| 181 | +- Use `BEGIN_PROLOG` / `END_PROLOG` for all reusable tables |
| 182 | +- Use `@local::` for prolog references; `@table::` for struct inheritance/merging |
| 183 | +- Do not duplicate full table bodies — inherit with `@table::Base` then override fields |
| 184 | +- One `#include` per dependency; include Offline prologs before EventNtuple prologs |
| 185 | +- Comment all numeric constants with units: `MaxDE : 500.0 # MeV` |
| 186 | +- Boolean flags: `true` / `false` (lowercase, no quotes) |
| 187 | +
|
| 188 | +--- |
| 189 | +
|
| 190 | +## Adding a New Branch (Developer Checklist) |
| 191 | +
|
| 192 | +See `doc/developers.md` for the full walkthrough. Summary: |
| 193 | +
|
| 194 | +1. Create `inc/FooInfo.hh` with commented struct (see struct pattern above) |
| 195 | +2. Add fill logic to `src/InfoStructHelper.cc` (or `InfoMCStructHelper.cc` for MC) |
| 196 | +3. Register branch in `src/EventNtupleMaker_module.cc` |
| 197 | +4. Add prolog table to `fcl/prolog.fcl` using `@table::` inheritance |
| 198 | +5. Regenerate `doc/branches.md`: `ntuplehelper --list-all-branches --export-to-md` |
| 199 | +6. Run `bash validation/quick_test.sh` to confirm no runtime errors |
| 200 | +
|
| 201 | +--- |
| 202 | +
|
| 203 | +## Key Files Reference |
| 204 | +
|
| 205 | +| File | Role | |
| 206 | +|---|---| |
| 207 | +| `src/EventNtupleMaker_module.cc` | Main `art::EDAnalyzer`; branch registration and filling | |
| 208 | +| `src/InfoStructHelper.cc` | Reco → struct fill logic | |
| 209 | +| `src/InfoMCStructHelper.cc` | MC truth → struct fill logic | |
| 210 | +| `inc/*Info*.hh` | One ntuple branch struct per file | |
| 211 | +| `fcl/prolog.fcl` | Master FHiCL prolog (track types, branch configs, MC options) | |
| 212 | +| `fcl/from_mcs-mockdata.fcl` | Default/reference job FCL | |
| 213 | +| `helper/ntuplehelper.py` | Python ntuple inspection tool | |
| 214 | +| `rooutil/inc/RooUtil.hh` | ROOT analysis library | |
| 215 | +| `rooutil/roodask/roodask.py` | Dask distributed job runner (single-file) | |
| 216 | +| `validation/quick_test.sh` | Primary smoke test | |
| 217 | +| `doc/developers.md` | Developer guide for adding branches | |
| 218 | +
|
| 219 | +--- |
| 220 | +
|
| 221 | +## roodask Sub-tool (rooutil/roodask/) |
| 222 | +
|
| 223 | +See `rooutil/roodask/.github/copilot-instructions.md` for detailed architecture. |
| 224 | +Key points: single-file CLI, auto-generates C++ `main()` wrapper, shared NFS filesystem, |
| 225 | +full environment propagated to Dask workers. Do not split `roodask.py` into multiple files. |
0 commit comments