Repository navigation
Gen2 Production PR: Manual Fix for GENIE timing bug in CAFMaker + Extend existing-data shift in CAFMaker - #675
Conversation
…ycenterFM, fmatch variants, and CRUMBS features Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxqnx6r8HqPPywzXcxBahz
…Maker Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxqnx6r8HqPPywzXcxBahz
…coped reco objects Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxqnx6r8HqPPywzXcxBahz
Fields shifted by CorrectMCTiming()/SBNDShiftCRTReference()/SBNDShiftPMTReference()/ FixPMTReferenceTimes() came from optional slice-scoped associations (OpT0Finder candidate, TPCPMTBarycenterMatch, SimpleFlashMatch, CRUMBSResult) that aren't guaranteed to exist for a given slice. Shifting an absent association's field turned its honest "not filled" sentinel into a bogus near-default value that looked filled but wasn't. Each field is now guarded against its own struct's actual unfilled convention (NaN for structs with no setDefault(), -9999. for setDefault()-based structs and the upstream SimpleFlashMatch producer's own sentinel), confirmed individually from source and empirically rather than assumed uniform.
CorrectMCTiming() recovers a dk2nu-specific bug (missing meson decay time in the stored vertex time) and so should only ever touch interactions that came from GENIE. But rec.mc.nu is filled from whatever generator produced MCTruth under the configured producer label, not GENIE specifically -- HNL/dissonant- Higgs productions (MeVPrtlGen) reuse the same "generator" label and push their own MCTruth there too. The correction happened to still skip those in current production configs, but only because MeVPrtlGen never produces the bsim::Dk2Nu product the correction depends on -- incidental, not an explicit guarantee. Sets SRTrueInteraction.generator (declared, previously never assigned) from whether a simb::GTruth was associated with the interaction -- GTruth only ever comes from GENIE -- and gates the correction on it directly.
SBNDShiftPMTReference() was shifting slc.correctedOpFlash.OpFlashT0/ .OpFlashT0Corrected by the SBND frame shift, but for data these fields are already reference-frame corrected upstream: LightPropagationCorrection_module.cc computes them from the real SPECTDC RWM/trigger timestamps in its data-only (!fIsMC) branch. Shifting them again here double-corrected the reference time. Same category as soft_trig.flash_peaktime/sbnd_crt_veto.sp_time, already left alone for this reason. MC is unaffected: for MC that module's !fIsMC branch is skipped, so correctedOpFlash is unshifted at the producer level and CorrectMCTiming()'s own dk2nu-offset correction of it remains necessary.
…default simb::MCFlux::fdk2gen defaults to -999. (its own Reset()), a different sentinel than the -9999. convention every other guard in this function checks. Without this, an interaction with an unfilled dk2gen would compute a wrong-but-not-NaN offset_ns and silently corrupt every field CorrectMCTiming shifts, the same failure class as the earlier -9999. guard fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tions Trim CorrectMCTiming()/SBNDShiftCRTReference()/SBNDShiftPMTReference()/ FixPMTReferenceTimes() comments from prose paragraphs to short per-field labels, and record that SRSBNDCRTVeto.sp_time, SRSoftwareTrigger.flash_peaktime, and correctedOpFlash.OpFlashT0/.OpFlashT0Corrected (data path) are already reference-frame corrected upstream in their own producer modules (CRTVeto, SoftwareTrigger, LightPropagationCorrection) rather than left as an open watch-list item. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nerator Previously any MCTruth without an associated GTruth (i.e. non-GENIE) was tagged kUnknownGenerator, even though MeVPrtlGen_module.cc pushes a matching MeVPrtlTruth into the same event at the same index as its MCTruth (confirmed in its produce(): the two collections are always pushed back-to-back in the same loop iteration). Use that index alignment -- the same assumption this function already relies on for mcfluxes.at(i)/dk2nus.at(i) -- to tag those entries kMeVPrtl instead. caf::kMeVPrtl already existed in SREnums.h but nothing was setting it. CorrectMCTiming()'s existing `generator != caf::kGENIE` guard needs no change: kMeVPrtl still isn't kGENIE, so those slices are still skipped. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This function runs unconditionally for both MC and data with no detector gate (called for ICARUS too), so guarding it is out of scope for this SBND-only dk2nu/gate-shift project -- back to unconditionally shifting fmatch.time/barycenterFM.flashTime/.flashFirstHit, matching pre-guard-pass behavior byte-for-byte (comment included), so ICARUS CAF output is unaffected.
Companion to the sbnanaobj SRMeVPrtl::prod_time field: sources it from MeVPrtlTruth::meson_dpos_beamcoord.T(), the clean pre-beam-offset meson decay time (already threaded from the BNB flux ntuple's ini_t[0]). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Hi @tjones79 and/or @PetrilloAtWork can you guys have a look to review? I have attached the docdb that has validation plots to cover all the expected changes (and also unchanges). |
tjones79
left a comment
There was a problem hiding this comment.
Just the one comment. Everything else looks good to me. Thanks :)
| offset_ns[i] = nu.prod_time + nu.dk2gen * 100. / kSpeedOfLight; | ||
| double const delta_us = offset_ns[i] / 1000.; | ||
|
|
||
| nu.time += delta_us; |
There was a problem hiding this comment.
Looking at your validation plots (Thank you very much for these; they are extremely helpful!!)...
Your plots for rec.mc.nu.time and the prim truth times (Slides 7 and 8) show a clear ~350 ns shift. But the current code in the PR adds delta_us to these fields, which, naively, I would expect only to shift them by ~0.35 ns. Should this not actually be += offset_ns[i]?
(Looking at your other PR: https://github.com/SBNSoftware/sbnanaobj/pull/200/changes#diff-14e572d921caad14ab8a3f1b14c6af56c1dec7e9d457dce906c7024ed0e89d1bL57 The interaction time is also explicitly defined in ns in your comment)
There was a problem hiding this comment.
The units are in us so the correction offset is ~350ns or ~0.35us.
I corrected the comments and also clearly labelled the units of the offset variables
PetrilloAtWork
left a comment
There was a problem hiding this comment.
I feel I am missing an important point of this PR.
It unconditionally shifts all (or many) times to the time reference of the decay of the meson which originated the neutrino/particle. Is that correct? if so, why?
I see the mention of a bug in dk2nu, but it's not clear to me how this correction would leave alone input data not affected by that bug.
I have left some technical comments across the code. Once those are resolved, for me this PR is good to go in the target branch. But until the questions above are answered, I think the PR in this state should not be merged into develop.
| for (SRTrueParticle &part: nu.prim) { | ||
| if (part.genT > -9998.) part.genT += delta_us; | ||
| if (part.startT > -9998.) part.startT += delta_us; | ||
| if (part.endT > -9998.) part.endT += delta_us; |
There was a problem hiding this comment.
For your information: I have introduced an helper that should retrieve the default value of a field (in this case the test would look would look like: part.genT != caf::SRDefaults::For(part).genT).
As usual in C++, it is a mouthful but it saves you from putting magic values in the code.
However, that helper is not integrated in the branch you are targeting (and I think not yet in develop either).
There was a problem hiding this comment.
I can't locate SRDefaults in the production/sbnd-gen2 branch so will keep this guard.
- Fill SRTrueInteraction.baseline/dk2gen natively in cm (util::kMeterToCentimeter) instead of converting with a magic *100 inside CorrectMCTiming - Replace CorrectMCTiming's duplicated truth-time shift blocks with shiftMCParticleTimes/shiftTruthTimes lambdas, reused for both rec.mc.nu and the slice-matched slc.truth - Swap the local kSpeedOfLight constant for util::kc; add Doxygen docs on CorrectMCTiming - Demote the CorrectMCTiming and fSubRunPOT/frameApplyAtCaf debug std::cout prints to mf::LogDebug - Fetch dk2nu via a raw product handle instead of an art::Ptr vector; use fParams.GenLabel() for both the mcflux and dk2nu handle fetches instead of a hardcoded "generator" string - const-qualify the placeholder baddk2nu; de-duplicate the generator_ enum assignment via a local reference Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Converts dk2nu's raw ns-valued ancestor startt to us at fill time, matching the README's Time=us convention and the sibling time field's existing ns->us fill pattern. CorrectMCTiming()'s offset formula reworked to match: prod_time is multiplied back by 1000 to recombine with the still-ns-valued dk2gen/util::kc term, so offset_vector_ns keeps its existing ns meaning unchanged -- only how it's assembled changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2989cb1 to
9246e33
Compare
This PR targets the
In the scope of In the
Inside-function: |
Description:
Validation:
See slide 1-26 on docdb#49588
Dependency:
Gen2 Production:
sbncode: #675
sbnanaobj: SBNSoftware/sbnanaobj#199
sbndcode: SBNSoftware/sbndcode#967
Develop:
sbncode: #676 -- to mirror description bullet 1/2/3
sbnanaobj: SBNSoftware/sbnanaobj#200 -- to mirror description bullet 1/2/3
Checklist: