Skip to content

High-KE capture proton trigger bands (apr_proton_p335/p250/p150) - #191

Merged
michaelmackenzie merged 7 commits into
Mu2e:mainfrom
michaelmackenzie:high-ke-protons
Nov 11, 2026
Merged

michaelmackenzie merged 7 commits into
Mu2e:mainfrom
michaelmackenzie:high-ke-protons

Conversation

@michaelmackenzie

@michaelmackenzie michaelmackenzie commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Prescaled momentum bands for measuring the energy spectra of charged particles emitted after muon capture (protons, deuterons, tritons, He-3, alpha). The three paths share one helix finder for these particles and run no track fit. There is no particle-ID cut in the trigger: species are separated offline.

Paths

path bit helix p [MeV/c] phys PS val/mds PS
apr_proton_p335 (was apr_proton) 270 > 335 1 100
apr_proton_p250 271 > 250 1 800
apr_proton_p150 272 > 150 1 40000
  • Common helix cuts (AprProtonHelixSelection): nsh ≥ 20 (15 for p150), 40 < |λ| < 2000 mm, |d0| < 600 mm.
  • Old cut was too tight: the old maxAbsLambda: 1000 removed most of the signal, because these protons are very forward (|λ| ≈ 1000 mm).
  • No particle-ID cut:
    • Straw-hit edep is not used: it is compressed above ~4 keV and capped by the ADC near 5.8 keV.
    • A mass from p and the helix t–z slope (HelixFilter maxMass) was tried. It removes only 1–3% of KE > 60 MeV protons, but it depends on the hit-time calibration, and it removes the deuterons and tritons, whose spectra are also of interest.
  • Finder limits: TrigAprProtonHelixFinder raises maxHelixMomentum / maxHelixPerpMomentum to 800 / 300 MeV/c, so deuterons and tritons are not cut by the finder.
  • Off spill: all three paths are disabled off spill.
  • physMenu: the bands run unprescaled for now, as most physMenu paths do (about 89 kHz in total from this simulation, set by p150).
  • val/mds prescales: these are nested integer multiples. PrescaleEvent keeps events with event % PS == 0, so every event a band keeps also carries the higher bands' filter decisions, and their turn-ons can be measured from data. The total is about 23 Hz with the simulation below.
  • useTrackerStatus: it is now set in the p250 and p150 HelixFilters, where it is a required parameter.

Expected performance

These numbers are from simulation: MDC2025 capture-particle samples without pileup mixing, and NoPrimaryMix1BB MDC2025au for the rates.

path rate before PS p (KE > 60) d (KE > 20) t (KE > 20)
p335 ~1.3 kHz 0.17 0.02 0.02
p250 ~7.3 kHz 0.45 0.11 0.09
p150 ~89 kHz 0.52 0.34 0.25
  • In the p335 band about half of the selected helices are deuterons from the pileup; in p150, about 80% are protons.
  • He-3 and alpha are accepted only in p150, with efficiencies of 0.09 and 0.06.
  • The pileup simulation has no triton or alpha generator, so rates from those species are not included.

Tests

  • fhicl-dump of test/triggerTest.fcl and test/triggerMDSTest.fcl with the regenerated menus.
  • mu2e on 100 signal events with the phys and val menus: all three paths run and are prescaled as configured.
  • triggerMDSTest.fcl fails at configuration on main as well: mdsMenu.json lists calo_MVANNCE, but that path is now called calo_NNCE. This PR doesn't touch it.
  • CI: the reference log is expected to differ on the renamed and new apr_proton* paths, and will need updating after merge.

🤖 Generated with Claude Code

michaelmackenzie and others added 6 commits October 21, 2026 14:10
… menus

- apr_proton -> apr_proton_p335 (bit 270) so all three proton bands carry their
  momentum cut in the name; modules renamed to AprProtonP335PS/HSFilter/TriggerInfoMerger
- add apr_proton_p335/p250/p150 (bits 270-272) to valMenu and mdsMenu with nested
  OnSpill prescales 25/400/20000 (~24 Hz total), disabled OffSpill
- set useTrackerStatus in AprProtonP250HSFilter and AprProtonP150HSFilter (now a
  required HelixFilter parameter)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- drop maxMass 1200 from AprProtonHelixSelection: the bands now accept all charged
  capture particles (p, d, t, He3, alpha), separated offline. The cut removed only
  1-3% of KE>60 protons but depends on the hit-time calibration.
- valMenu/mdsMenu OnSpill prescales 25/400/20000 -> 100/800/40000 (~23 Hz)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@oksuzian oksuzian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary — #191

Reviewed at head 25c38f11c812c77fc272ed4de607216dae2b9859. First pass.

Decision

  • 🟡 comment, with one S1 first. The PR changes the trigger paths that this repository's CI reference log records, but it does not update ci/reference_log_file.log. The description plans that update for after the merge. Until it lands, the trigger row of Offline's FNALbuild report fails on every Offline PR (finding 1). That row is allowed to fail, so it blocks no merge and no job, which is why this is a comment and not a request for changes. Committing the new log here closes the gap, and I would approve with it. Everything else I checked matches the description: the names and bits, the cuts, the prescales, the nesting, and the generated menus. The new paths run in the phys, val and mds menus, and no other path changes its counts.

Scope understood

  • Renames apr_proton (bit 270) to apr_proton_p335, and adds apr_proton_p250 (271) and apr_proton_p150 (272). They are helix-momentum bands above 335, 250 and 150 MeV/c, for the spectra of charged particles from muon capture. They share one proton helix finder and run no track fit.
  • There is no particle-ID cut. The last commit, 25c38f1, removed maxMass: 1200, so deuterons, tritons and heavier particles are kept and separated offline.
  • The paths are disabled off spill. On spill the prescales are 1/40/2000 in the phys menu and 100/800/40000 in the val and mds menus.

Findings

  1. 🟠 [S1] ci/reference_log_file.log is not updated, so FNALbuild's trigger row will fail on every Offline PR after the merge

    • Evidence: the reference log is unchanged at this head. It still has apr_proton and its modules (ci/reference_log_file.log:443, 680, 1010-1025). The checker counts a path that is in only one of the two logs as a major change (ci/check_trigger_results.py:164-170), and then exits 2.
    • I ran test/triggerTest.fcl on the 20,000 events of ci/data_files.txt, then ci/check_trigger_results.py against the reference log. Both workdirs use the Offline CI build main/7fb240fc_ec057b9e_7b20730e.
      • With this repository's main, the checker exits 0 with "No problems detected". 5,642 events pass, as in the reference.
      • At this head it exits 2: "New trigger path apr_proton_p335 in local found, not in the reference!" (the same for p250 and p150), and "Trigger path apr_proton in reference not found in local!".
      • At this head, the other 51 paths match main count for count. Timing is within the limits (|time_new / time_ref − 1| = 0.089).
    • Impact: FNALbuild's trigger job runs this check on every Offline PR, using this repository's main (codetools bin/github/jenkins_tests/mu2e-offline-build-test/build.sh:236-256).
      • Exit 2 marks the row ❌ "Return Code 2".
      • trigger is in FAIL_OK (job.sh:24), so the failure does not fail mu2e/buildtest or block merges.
      • But the row stays red on every Offline PR until the log is updated, and a real trigger regression in that time would look the same. The last such gap, after Offline#2129, lasted 18 hours: from 02:37 to 20:38 UTC on 2026-11-10, when #190 updated the log.
    • Suggested fix: produce the log at this head the way ci/update_reference_log.py --local does, and commit it in this PR. That script opens its own PR from main, so here commit the log it produces rather than letting it open the PR. The diff of the log would also show reviewers exactly which path counts this PR changes.
  2. 🟡 [S2] The phys menu turns apr_proton_p335 on with no prescale, but the body does not give the phys-menu rate

    • On main, apr_proton is disabled on spill (prescale -1 at data/physMenu.json:262). At this head, apr_proton_p335 has prescale 1 (data/physMenu.json:262).
    • The body gives the val/mds total, about 23 Hz. By the body's own table the phys total is about 1.5 kHz: 1.3 kHz from p335 alone, plus 7.3 kHz / 40 and 89 kHz / 2000.
    • On the CI sample, the three paths add 155 events to the phys menu's passed total: 5,642 on main and 5,797 here. The sample is a skim of pileup events, so this is not a rate.
    • Please state the phys-menu total in the body, and say whether the trigger group has agreed to it.

Checked, no action needed

  • 🟢 Names and bits. Bits 270, 271 and 272 are the same three paths in physMenu.json, valMenu.json and mdsMenu.json. No menu has a duplicate bit or path name; I checked all five with a JSON parser.

    • At this head, apr_proton and the old module names appear only in ci/reference_log_file.log.
    • Offline main (7fb240fc) and Production main (ec057b9e) contain neither apr_proton nor AprProton (git grep). GitHub code search over the Mu2e organization finds apr_proton only in this repository.
  • 🟢 Generated menus. muse build exits 0 at main and at this head. fhicl-dump exits 0 in both workdirs for all five test configs. It also exits 0 for the phys, val and mds menus in their on-spill, off-spill and combined forms (review-only copies of triggerTest.fcl pointed at each generated menu).

  • 🟢 Offline parameters, from Offline main 7fb240fc:

    • useTrackerStatus is a required fhicl::Atom<bool> with no default (TrkFilters/src/HelixFilter_module.cc:243). All 17 helix filters in core/filters/trigAprFilters.fcl set it.
    • maxMass is an optional cut (HelixFilter_module.cc:68). No maxMass or minMass setting remains in the PR's files. The word appears only in a comment (core/filters/trigAprFilters.fcl:784), and fhicl-get finds neither key in any of the six selections.
    • The helix finder's defaults are 400 and 140 MeV/c (CalPatRec/fcl/prolog.fcl:556,558). The new limits, 800 and 300 MeV/c (core/producers/trigAprProducers.fcl:36-37), apply only to TrigAprProtonHelixFinder. The electron finder TrigAprHelixFinder keeps 400 and 140.
  • 🟢 Final configuration, read with fhicl-get from the generated configs:

    • Both helicity selections of all three filters have |λ| from 40 to 2000 mm and d0 from −600 to 600 mm.
    • The momentum thresholds are 335, 250 and 150 MeV/c.
    • The minimum straw-hit counts are 20, 20 and 15.
    • The cuts inherited from AprHelixSelections_pos are the same as on main: χ²(XY) and χ²(φZ) at most 8, hit ratio at least 0.4, at most 30 loops, and momentum at most 2000 MeV/c.
    • Off spill, the prescale is -1 for all three paths in all three menus.
    • doHelicityCheck changes from true to false, which the body does not mention. It has no effect: HelixFilter::checkHelixFromHelicity already sends each helix to the selection for its own helicity (HelixFilter_module.cc:318-327), so the check at :131 cannot fail.
  • 🟢 Nesting. PrescaleEvent keeps an event when e.event() % prescale == 0 (Trigger/src/PrescaleEvent_module.cc:106). A prescale of -1 becomes 0, which rejects every event (:50, :106).

    • In the val and mds menus, 800 = 8 × 100 and 40000 = 50 × 800. In the phys menu, 2000 = 50 × 40, and p335 has prescale 1.
    • So an event that passes a lower band's prescale also passes the higher bands' prescales, and their helix filters run on it.
  • 🟢 Jobs on the 20,000 CI events (skim_NoPrimary1BB_1a/1b.art, pileup only):

    path filters pass, prescale set to 1 phys menu: prescale pass / path pass val menu: prescale pass / path pass
    apr_proton_p335 249 20,000 / 249 200 / 1
    apr_proton_p250 1,422 500 / 33 25 / 3
    apr_proton_p150 12,770 10 / 8 0 / 0
    • The time-cluster filter passes 17,748 of the 20,000 events when every prescale is 1.
    • In the val run, all 43 other paths have the same counts as main. The phys comparison is under finding 1.
    • The proton helix finder costs 0.20 ms per event. The full-event average is 27.0 ms at this head and 27.1 ms on main, with both runs at the same time on the same machine.

Not a finding: test/triggerMDSTest.fcl already fails on main

  • Confirmed. At main and at this head it exits 9 at configuration: "Unknown path calo_MVANNCE has been specified in 'trigger_paths'". data/mdsMenu.json:274 on main names calo_MVANNCE, while the path is calo_NNCE (core/trigSequences.fcl:257).
  • To check that this PR adds no new failure behind that error, I made a review-only copy of the test that defines calo_MVANNCE as calo_NNCE. With it, both versions run 2,000 events with exit 0, and 4 events pass. The other 51 paths match, and the three new paths run.
  • Open #188 by the same author deletes the calo_MVANNCE entry, which fixes this.

Merge order (not a finding)

  • git merge-tree merges this head cleanly with open #186, #188 and #189. With #188, the merged mds menu has 53 paths and no duplicate bits.

Validation check

  • Build/tests run: yes.
    • Two muse workdirs backed by the Offline CI build main/7fb240fc_ec057b9e_7b20730e (Offline main 7fb240fc, Production main ec057b9e, this repository's main 7b20730e). One has this repository at main, the other at this head. Both build with exit 0.
    • Jobs: triggerTest.fcl on all 20,000 CI events in both workdirs, followed by ci/check_trigger_results.py; the val menu on all 20,000 events in both; the three proton paths with prescale 1 on all 20,000 events; and triggerMDSTest.fcl and the patched copy in both.
    • ci/check_trigger_results.sh itself was not run. I ran its two steps, mu2e and the Python checker, separately.
  • CI at this head: the five GitHub checks pass. They are whitespace, format, two single-package builds and a build against develop, completed 21:36-21:43 UTC. There are no commit statuses. None of these runs a mu2e job, and this repository defines no tests for make test. The trigger comparison runs only in Offline's FNALbuild, against this repository's main, so it cannot see this PR before the merge.
  • Config contract check: pass.
  • Cross-repo consistency: pass. Nothing in Offline or Production uses the old or new names.

Residual risk

  • I did not reproduce the efficiencies or rates in the body, and I did not run a signal sample.
  • The CI sample is a skim, so its pass fractions are not rates. As a loose cross-check, its ratio of p250 to p335 before the prescale is 5.7 (1,422 / 249), against 5.6 (7.3 / 1.3) in the body. For p150 to p335 it is 51, against 68 in the body.
  • I did not run a raw-data job.

Author follow-ups

  1. Commit the updated ci/reference_log_file.log, produced at this head (finding 1).
  2. State the phys-menu total rate of the three bands in the body, and whether the trigger group has agreed to it (finding 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michaelmackenzie

michaelmackenzie commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator Author

Responding to the AI comments:

  • S1: I update the CI log after PRs, as it's costly to generate per PR and then need to re-generate with every PR that is merged before it and generates conflicts.
  • S2: I removed the prescales, as for physics signatures it's best to not prescale simulation and only to set them for real data-taking. The val and mds menus have more realistic prescale values.

@oksuzian oksuzian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary — #191

Reviewed at head 60c82a990ac472bb2efda740dffe18c5edfefce7. Re-review of 25c38f1.

Decision

  • 🟢 approve. Thanks for the answers; both findings are settled. You keep the reference log out of this PR on purpose, and the phys menu now follows the convention that physics paths run unprescaled in simulation. I checked that this does not grow Production's on-spill digitization output.

What changed since 25c38f1

  • One commit, 60c82a9 "physMenu: run the proton bands unprescaled" (data/physMenu.json, +2 −2). It sets the on-spill prescale of apr_proton_p250 from 40 to 1 and of apr_proton_p150 from 2000 to 1. apr_proton_p335 was already 1. Not a rebase.

Prior findings (from my review at 25c38f11)

  • 🟢 [was 🟠 S1] ci/reference_log_file.log is not updated. Answered, not changed, and I accept the reason: you regenerate the log after merges, because doing it per PR costs a full run and conflicts with every PR merged before it.
    • After the merge, Offline FNALbuild's trigger row will show "Return Code 2" until your update lands. It is in FAIL_OK, so it blocks nothing, and I will read it as expected in that window.
  • 🟢 [was 🟡 S2] The phys-menu rate was not stated. Addressed differently: the three bands are now unprescaled in the phys menu, as you explain.
    • This matches the rest of the menu. At this head, 48 of the 54 phys paths have on-spill prescale 1. Only minBias_SDCount, minBias_CDCount and calo_N0Source are prescaled (10000), and 3 are off.
    • It does not grow Production's on-spill digitization output. JobConfig/digitize/OnSpill.fcl:11 writes only events from Digitize.SignalTriggers (cpr_Trk*_80*, apr_Trk*_80*, prolog.fcl:235) and the triggerable paths, and no apr_proton_* name matches either.
    • The shared proton helix finder already ran on every event at 25c38f11, because apr_proton_p335 had prescale 1 there. So the commit adds only the two HelixFilter decisions per event.

Validation check

  • Build/tests run: a muse workdir at this head on the Offline CI build main/7fb240fc_ec057b9e_7b20730e. muse build exits 0. fhicl-dump exits 0 for all 16 configs from the first pass: the five test configs, the nine menu wrappers, and the two job wrappers.
    • Against the same dumps at 25c38f11, only two lines differ: AprProtonP250PS and AprProtonP150PS prescale become 1. They show up in menu_physMenu_OnSpill and triggerTest, which uses the phys menu.
    • The val and mds menus and the phys off-spill menu are identical.
  • No jobs were rerun. The commit changes only two prescale values, and my first-pass run with the three bands at prescale 1 already covers this setting (249, 1,422 and 12,770 of the 20,000 CI events).
  • The five GitHub checks pass at this head (checked 01:20 UTC).
  • Config contract check: pass.
  • Cross-repo consistency: pass. Nothing in Offline or Production names the old or new paths.

Residual risk

  • The efficiencies and rates in the description were not reproduced.
  • test/triggerMDSTest.fcl still fails on main and here (calo_MVANNCE); #188 fixes that.

@michaelmackenzie
michaelmackenzie merged commit 1dc8ee4 into Mu2e:main Nov 11, 2026
5 checks passed
@michaelmackenzie
michaelmackenzie deleted the high-ke-protons branch November 11, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants