Skip to content

Commit 65e2741

Browse files
committed
Replace cout with error when mismatch found between CRV reco hits and CRV MC truth info. Updated fcl formatting and validation scripts to match main.
2 parents 35a6d96 + 0430796 commit 65e2741

29 files changed

Lines changed: 1366 additions & 1123 deletions

AGENTS.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
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.

fcl/CompareDeTracks.fcl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,5 @@ physics.end_paths : [ EndPath ]
5555
physics.producers.TrkQualDe.KalSeedPtrCollection : "MergeKKDeCalib"
5656
services.TimeTracker.printSummary: true
5757
services.TFileService.fileName: "nts.owner.EventNtupleDeCalib.version.sequence.root"
58-
physics.analyzers.EventNtuple.KalSeedMCAssns : @nil # module which created the MC truth matching (KalSeedMC). including both primary and 2ndary tracks.
58+
physics.analyzers.EventNtuple.trk.mc.kalSeedMCAssns : @nil # module which created the MC truth matching (KalSeedMC). including both primary and 2ndary tracks.
5959

fcl/TrkAnaLineFromDigis.fcl

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,7 @@ physics :
3838
@table::EventNtupleMaker
3939
FitType : KinematicLine
4040
diagLevel : 2
41-
FillMCInfo : true
42-
FillTrkPIDInfo : false
43-
FillHitInfo : true
4441
FillTriggerInfo : false
45-
branches : [
46-
{ input: "KKLine"
47-
branch : "kl"
48-
options : { fillMC : true genealogyDepth : 5 }
49-
}
50-
]
51-
ExtraMCStepCollectionTags : [ "compressDigiMCs:protonabsorber", "compressDigiMCs:stoppingtarget" ]
5242
}
5343

5444
@table::TrkAnaReco.analyzers
@@ -81,6 +71,13 @@ physics.producers.KKLine.ModuleSettings.SaveAllFits : true
8171
physics.producers.KKLine.ExtensionSettings.BFieldCorrection : false
8272

8373
physics.end_paths : [ EndPath ]
74+
physics.analyzers.TrkAnaLine.trk.fits : [
75+
{ input: "KKLine"
76+
branchname : "kl"
77+
options : { genealogyDepth : 5 }
78+
}
79+
]
80+
physics.analyzers.TrkAnaLine.mcsteps.extraMCStepTags : [ "compressDigiMCs:protonabsorber", "compressDigiMCs:stoppingtarget" ]
8481
services.TimeTracker.printSummary: true
8582
services.TFileService.fileName: "nts.owner.TALineDigis.version.sequence.root"
8683
#include "Offline/CRVResponse/fcl/epilog_extracted.fcl"

fcl/from_dig-OnSpill.fcl

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,24 @@ physics : {
1616
# apr
1717
EventNtupleTTMCApr : {
1818
@table::EventNtupleMakerTTMC
19-
branches : [ {
20-
@table::TTMCBranch
21-
input : "MergeTTApr"
22-
} ]
23-
KalSeedMCAssns: "TTAprKSFMC"
2419
RecoCountTag : "TTAprKSFMC"
2520
SelectEvents : [ "Digitize:apr_highP*" ]
2621
}
2722
# tpr
2823
EventNtupleTTMCTpr : {
2924
@table::EventNtupleMakerTTMC
30-
branches : [ {
31-
@table::TTMCBranch
32-
input : "MergeTTTpr"
33-
} ]
34-
KalSeedMCAssns: "TTTprDeKSFMC"
3525
RecoCountTag : "TTTprDeKSFMC"
3626
SelectEvents : [ "Digitize:tprDe_highP*" ]
3727
}
3828
# cpr
3929
EventNtupleTTMCCpr : {
4030
@table::EventNtupleMakerTTMC
41-
branches : [ {
42-
@table::TTMCBranch
43-
input : "MergeTTCpr"
44-
} ]
45-
KalSeedMCAssns: "TTCprDeKSFMC"
4631
RecoCountTag : "TTCprDeKSFMC"
4732
SelectEvents : [ "Digitize:cprDe_highP*" ]
4833
}
4934
# mpr
5035
EventNtupleTTMCMpr : {
5136
@table::EventNtupleMakerTTMC
52-
branches : [ {
53-
@table::TTMCBranch
54-
input : "MergeTTMpr"
55-
} ]
56-
KalSeedMCAssns: "TTMprDeKSFMC"
5737
RecoCountTag : "TTMprDeKSFMC"
5838
SelectEvents : [ "Digitize:mprDe_highP*" ]
5939
}
@@ -64,6 +44,15 @@ physics : {
6444
}
6545
#physics.analyzers.TAapr.InfoMCStructHelper.SimParticleCollectionTag: "compressDetStepMCs"
6646

47+
physics.analyzers.EventNtupleTTMCApr.trk.fits : [ { @table::TTMCBranch input : "MergeTTApr" } ]
48+
physics.analyzers.EventNtupleTTMCApr.trk.mc.kalSeedMCAssns : "TTAprKSFMC"
49+
physics.analyzers.EventNtupleTTMCTpr.trk.fits : [ { @table::TTMCBranch input : "MergeTTTpr" } ]
50+
physics.analyzers.EventNtupleTTMCTpr.trk.mc.kalSeedMCAssns : "TTTprDeKSFMC"
51+
physics.analyzers.EventNtupleTTMCCpr.trk.fits : [ { @table::TTMCBranch input : "MergeTTCpr" } ]
52+
physics.analyzers.EventNtupleTTMCCpr.trk.mc.kalSeedMCAssns : "TTCprDeKSFMC"
53+
physics.analyzers.EventNtupleTTMCMpr.trk.fits : [ { @table::TTMCBranch input : "MergeTTMpr" } ]
54+
physics.analyzers.EventNtupleTTMCMpr.trk.mc.kalSeedMCAssns : "TTMprDeKSFMC"
55+
6756
end_paths : [ EndPath ]
6857
physics.trigger_paths : [ "TrigPath" ]
6958
services.TFileService.fileName: "nts.owner.trkana-triggerMC.version.sequencer.root"

fcl/from_dig-calo.fcl

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,33 @@ physics.end_paths : [ e1 ]
2424

2525
#Tags to fill PBI information
2626
#physics.analyzers.EventNtuple.PBTTag : "EWMProducer"
27-
#physics.analyzers.EventNtuple.PBTMCTag : "EWMProducer"
27+
#physics.analyzers.EventNtuple.mc.PBTMCTag : "EWMProducer"
2828
physics.analyzers.EventNtuple.RecoCountTag : ""
2929
physics.analyzers.EventNtuple.PBTTag : ""
30-
physics.analyzers.EventNtuple.PBTMCTag : ""
31-
physics.analyzers.EventNtuple.SimParticlesTag : "CaloShowerStepMaker"
30+
physics.analyzers.EventNtuple.mc.PBTMCTag : ""
31+
physics.analyzers.EventNtuple.mc.simParticlesTag : "CaloShowerStepMaker"
3232
#Turn off tracker and other branches
33-
physics.analyzers.EventNtuple.branches : [ ]
34-
physics.analyzers.EventNtuple.FillTrkQual : false
33+
physics.analyzers.EventNtuple.trk.fill : false
3534
physics.analyzers.EventNtuple.FillTriggerInfo : false
36-
physics.analyzers.EventNtuple.FillHitInfo : false
37-
physics.analyzers.EventNtuple.FillCRVCoincs : false
35+
physics.analyzers.EventNtuple.crv.fillCoincs : false
3836

3937
#Toggle calo branches
40-
physics.analyzers.EventNtuple.FillCaloClusters : false
41-
physics.analyzers.EventNtuple.FillCaloHits : false
42-
physics.analyzers.EventNtuple.FillCaloRecoDigis : false
43-
physics.analyzers.EventNtuple.FillCaloDigis : true
38+
physics.analyzers.EventNtuple.calo.fillClusters : false
39+
physics.analyzers.EventNtuple.calo.fillHits : false
40+
physics.analyzers.EventNtuple.calo.fillRecoDigis : false
41+
physics.analyzers.EventNtuple.calo.fillDigis : true
4442

45-
physics.analyzers.EventNtuple.CaloShowerSimTag : "CaloShowerROMaker"
46-
physics.analyzers.EventNtuple.CaloDigisTag : "CaloDigiMaker"
43+
physics.analyzers.EventNtuple.calo.mc.showerSimTag : "CaloShowerROMaker"
44+
physics.analyzers.EventNtuple.calo.digisTag : "CaloDigiMaker"
4745

4846
#Toggle calo MC branches
49-
physics.analyzers.EventNtuple.FillMCInfo : false
50-
physics.analyzers.EventNtuple.FillCaloMC : true
51-
physics.analyzers.EventNtuple.FillCaloClustersMC : false
52-
physics.analyzers.EventNtuple.FillCaloHitsMC : false
53-
physics.analyzers.EventNtuple.FillCaloSimInfos : false
54-
physics.analyzers.EventNtuple.FillCaloDigiSimInfos : true
55-
physics.analyzers.EventNtuple.FillCaloDigisMC : true
47+
# mc.fill is false so tracker/CRV MC and evtinfomc are disabled;
48+
# calo MC is controlled independently via calo.mc.fill (independent of mc.fill)
49+
physics.analyzers.EventNtuple.mc.fill : false
50+
physics.analyzers.EventNtuple.calo.mc.fillClusters : false
51+
physics.analyzers.EventNtuple.calo.mc.fillHits : false
52+
physics.analyzers.EventNtuple.calo.mc.fillSim : false
53+
physics.analyzers.EventNtuple.calo.mc.fillDigiSim : true
54+
physics.analyzers.EventNtuple.calo.mc.fillDigis : true
5655

5756
services.TFileService.fileName: "nts.owner.description.version.sequencer.root"

fcl/from_mcs-Run1B.fcl

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
# we start from mcs-extracted since Run-1B wants the straight line track fit
22
#include "EventNtuple/fcl/from_mcs-extracted.fcl"
33

4-
physics.analyzers.EventNtuple.FillMCInfo : true
5-
physics.analyzers.EventNtuple.StepPointMCTags : [ "compressRecoMCs:virtualdetector" ] # we add the mcsteps_virtualdetector branch
6-
physics.analyzers.EventNtuple.FillTimeClusterInfo : true
7-
physics.analyzers.EventNtuple.TimeClustersTag : "SimpleTimeCluster" # Store time clusters for straight line track finding
8-
physics.analyzers.EventNtuple.FillCaloMC : true
9-
physics.analyzers.EventNtuple.FillCaloClustersMC : true
10-
physics.analyzers.EventNtuple.FillCaloSimInfos : true
4+
physics.analyzers.EventNtuple.mcsteps.stepPointMCTags : [ "compressRecoMCs:virtualdetector" ] # we add the mcsteps_virtualdetector branch
5+
physics.analyzers.EventNtuple.timeclusters.fill : true
6+
physics.analyzers.EventNtuple.timeclusters.tag : "SimpleTimeCluster" # Store time clusters for straight line track finding
117
services.GeometryService.inputFile : "Offline/Mu2eG4/geom/geom_common.txt" # we can use the standard geometry
128

139
physics.EventNtupleEndPath : [ @sequence::EventNtuple.EndPath ] # add back genCountLogger

0 commit comments

Comments
 (0)