Skip to content

Commit 0430796

Browse files
Merge pull request #375 from AndrewEdmonds11/update-switches
Update fhicl configuration
2 parents a712d06 + b3fe57a commit 0430796

26 files changed

Lines changed: 1353 additions & 1112 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)