Step-2 D*/V0 (KS, Lambda) ntuplizer + 2D pointing constraint#44
Open
davidwalter2 wants to merge 15 commits into
Open
Step-2 D*/V0 (KS, Lambda) ntuplizer + 2D pointing constraint#44davidwalter2 wants to merge 15 commits into
davidwalter2 wants to merge 15 commits into
Conversation
Adds the step-2 chain for the KS->pi+pi- and Lambda0->p pi- alignment channels, mirroring the existing D*->D0 pi step-2 implementation. Step-2 reads the per-channel ALCARECO output (ALCARECOTkAlKsToPiPi / ALCARECOTkAlLambdaToProtonPi), re-pairs the deduplicated daughter tracks, applies V0Producer-like displaced-vertex cuts, then runs the existing ResidualGlobalCorrectionMakerTwoTrackKPiG4e CVH refit on the chosen pair. The ALCARECO format is treated as just "tracks + hits + clusters", with no assumption that pair structure or V0 candidate info is preserved -- the candidate finder rebuilds candidates from scratch. Files: * plugins/V0CandidateProducer.cc -- analog of DstToD0PiCandidateProducer for 2-body V0 decays. Iterates opposite-sign pairs from the input TrackCollection, runs KinematicParticleVertexFitter with configurable per-daughter masses, applies vertex p-value, mass-window, transverse pointing, and Lxy/sigma cuts using a fresh BeamSpot. For asymmetric decays (Lambda) tries both (m1, m2) and (m2, m1) assignments and keeps the one closer to the nominal V0 mass. Outputs the daughter tracks of the highest-pT surviving candidate in pair order, suitable for the downstream ntuplizer with respectTrackOrder=True. * plugins/BuildFile.xml -- adds DataFormats/BeamSpot dependency (V0 displaced-vertex cuts need the beamspot; D* candidate finder did not require it because the 3-body Delta-mass cut was sufficient). * python/V0CandidateProducer_cfi.py -- defaults tuned for KS; override daughterMass1, expectedV0Mass, mass window, and tryBothAssignments for Lambda. * python/ResidualGlobalCorrectionMakerTwoTrackPiPiG4e_cfi.py * python/ResidualGlobalCorrectionMakerTwoTrackProtonPiG4e_cfi.py -- per-channel cfi clones of the existing CVH ntuplizer C++ class (no new C++ -- the class is already mass-parameterised). The KS variant sets both daughter masses to the pion mass; the Lambda variant sets daughter1 to the proton mass and daughter2 to the pion mass, with respectTrackOrder=True. * test/runGlobalCorRecKsFromAlca.py * test/runGlobalCorRecLambdaFromAlca.py -- runners that load the corresponding cfis, produce a fresh offlineBeamSpot from the standard service (BeamSpot is not stored in the ALCARECO output), and chain V0CandidateProducer -> CVH ntuplizer. Validated on the existing TkAlKsToPiPi_8h.root and TkAlLambdaToProtonPi_8h.root files: KS peak at PDG within 0.001 MeV, Lambda peak at PDG within 0.5 MeV, both with ~5 MeV resolution. Note: a follow-up cleanup is planned to generalise ResidualGlobalCorrectionMakerTwoTrackG4e (the symmetric J/psi version) to take per-daughter masses via cfi, retire the K-pi-specific clone, and have one canonical class for all two-body decays. That refactor is orthogonal to this commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two cleanup steps building on the previous KS/Lambda step-2 commit:
1) Unify ResidualGlobalCorrectionMakerTwoTrackG4e and
ResidualGlobalCorrectionMakerTwoTrackKPiG4e into a single class.
The two classes shared >2700 lines of identical code; the K-Pi
variant was a clone-and-modify with three real additions on top of
the symmetric J/psi version: per-daughter masses (kaonMass/pionMass),
a respectTrackOrder flag, and an L1 trigger decision block. All three
are useful generically (not K-Pi specific), so the right move is to
keep the more general code and retire the original.
Concretely, ResidualGlobalCorrectionMakerTwoTrackG4e.cc is replaced
by the (renamed) K-Pi version with parameter names changed to be
channel-agnostic:
kaonMass -> daughterMass1
pionMass -> daughterMass2
kaonMassErr -> daughterMass1Err
pionMassErr -> daughterMass2Err
All new parameters (daughterMass1/2, daughterMass1/2Err, minPairMass,
maxPairMass, respectTrackOrder, doL1Trigger, l1Results, l1Triggers)
are made optional with backward-compatible defaults: muon mass for
the daughters, no pair-mass cut, no track-order constraint, no L1
filter. So the existing J/psi/Upsilon runner cfgs (which simply
don't pass any of these) reduce exactly to the previous behaviour.
ResidualGlobalCorrectionMakerTwoTrackKPiG4e.cc is deleted. The three
channel-specific cfi clones (KPi for D*, PiPi for KS, ProtonPi for
Lambda) are updated to instantiate the unified class with the new
parameter names; their content is otherwise unchanged.
2) Split the generic V0CandidateProducer_cfi into channel-specific
cfis matching the DstToD0PiCandidateProducer_cfi pattern.
V0CandidateProducer_cfi.py is replaced by:
KsToPiPiCandidateProducer_cfi.py (KS-tuned: both pions, symmetric)
LambdaToProtonPiCandidateProducer_cfi.py (Lambda-tuned: p+pi, asymmetric)
The C++ class V0CandidateProducer is unchanged; only the cfi
wrappers are now per-channel. Runners load the matching cfi.
Validation: all three V0 step-2 channels reproduce identical entry
counts and mass-peak positions before and after the refactor:
KS 1985 entries, peak at 0.4976 GeV (PDG 0.4976)
Lambda 807 entries, peak at 1.1162 GeV (PDG 1.1157)
D* 152 entries, peak at 1.860 GeV (PDG 1.8648)
The legacy J/psi runner cfg loads cleanly under the unified class
(falling back to muon-mass defaults via existsAs<>); a future bit-level
J/psi tree comparison would complete the validation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New cfi parameters (default off, no behaviour change for existing J/psi/Upsilon/D*/V0 runners): doPointingConstraint - enable the soft chi^2 pointing term pointingSigma - angular pointing resolution in radians (default 1e-3) The constraint requires the V0 transverse momentum to align with the flight vector from the beamspot to the secondary vertex. One scalar chi^2 term per event with sigma_g = pointingSigma * Lxy * |p_xy|, applied once at the start of the per-iteration constraint block (id == 0 guard); +1 ndof. Touches vertex-state indices 0..5 (daughter qop/lambda/phi) and 7..8 (vertex xy); index 6 (d0) and 9 (vertex z) are not coupled. Validated on KS (1985 -> 1949 entries, mass RMS 4.59 -> 4.56 MeV) and Lambda (807 -> 766 entries, RMS 17.6 -> 17.5 MeV). D0 chi^2 increases by +15 with no RMS gain — the BS reference is too coarse for prompt charm and a PV reference would be needed there. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds three optional InputTags to the CVH two-track ntuplizer:
dedxHarmonic2, dedxPixelHarmonic2 and dedxAllHarmonic2. When set, the
producer reads the corresponding ValueMap<float>s projected onto the
ALCARECO selected-track collection (via DeDxValueMapProjector in the
ALCARECO step) and fills new branches Mu{plus,minus}_dedx{,Pixel,All}Harmonic2
in the per-event tree.
The ALCARECO ValueMaps are keyed on the V0DaughterTrackProducer output
collection, which differs from the V0CandidateProducer output the CVH
ntuplizer reads downstream. Lookup is done by matching each fit track's
preserved TrackExtraRef.key() against the source collection (a new
dedxSourceTracks InputTag identifies that source). The joint estimator
read is independently optional via mayConsume so legacy ALCARECO inputs
without that ValueMap fall back to NaN per-track values.
Channel cfis (PiPi, ProtonPi, KPi) point at the matching ALCARECO
ValueMap names by default. Runners are repointed to the multifile
test ALCARECO output.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
D0/D* (DstToD0PiCandidateProducer): harmonize the D0, D*, and delta-m windows with the Stage-1 ALCARECO selector (AlignmentThreeBodyDecayTrackSelector in ALCARECOTkAlDstToD0Pi_cff.py): D0 mass to PDG +/-50 MeV [1.81483, 1.91483], D* mass to [1.860, 2.160], delta-m to [0.14243, 0.14843] (+/-3 MeV around 0.14543). The previous looser windows admitted swapped K/pi mass-assignment configurations that the ALCARECO already rejected. KS (KsToPiPiCandidateProducer): V0 mass window tightened from +/-100 MeV to [0.44, 0.56] (PDG +/-60 MeV) to suppress combinatorial background. Lambda (LambdaToProtonPiCandidateProducer): V0 mass window tightened from +/-65 MeV to [1.07, 1.16] (-46 MeV / +44 MeV around PDG) to suppress combinatorial background. KS and Lambda runner cfgs have their now-redundant override blocks removed -- they pick up the new tighter cfi defaults. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reverts the iteration to its pre-V0 form `for (auto jtrack = itrack + 1; ...)` and removes the parallel selection machinery added in 800021d: - useFixedPairOrder / firstTrack / secondTrack helpers - respectTrackOrder cfi parameter (and the ntuplizer member) - minPairMass / maxPairMass raw-mass cut and cfi parameter - opposite-sign charge filter (itrack->charge() + jtrack->charge() != 0) - now-obsolete `if (jtrack == itrack) continue;` (guaranteed by j > i) Selection responsibility now lives entirely in the candidate producers (V0CandidateProducer for KS/Lambda, DstToD0PiCandidateProducer for D0): the charge requirement, the post-vertex-fit V0/D0 mass window, the pointing and Lxy-significance cuts, and the choice of which track is slot 0 vs slot 1 are all enforced upstream. The CVH ntuplizer is now pure "refit + write tree", with no event-level selection of its own. Behaviorally unchanged for the V0 channels we currently run on (the 2 tracks per event are already pre-selected and ordered upstream). The `j > i` form also generalises naturally to >1 candidate per event (a deferred multi-candidate fix we plan in V0CandidateProducer). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…S veto V0CandidateProducer + DstToD0PiCandidateProducer: skip input tracks with isLooper() == true at the start of the candidate search (the CVH refit downstream cannot handle them and was discarding the events anyway). For DstToD0PiCandidateProducer this is applied to the K and D0-pion slots only -- the soft pion track is not propagated downstream by the CVH so its looper status is left as-is. V0CandidateProducer: replace the single legacy `daughterMassErr` with two independent parameters `daughterMass1Err`, `daughterMass2Err` (falling back to `daughterMassErr` and then 1e-6 for back-compat). The mass-error pairing follows the mass pairing in fitV0(), so for the Lambda channel's tryBothAssignments=True swap the proton-mass error stays paired with the proton mass even when applied to the swapped input track. V0CandidateProducer: add an optional K_S mass veto (`applyKsVeto`, `ksVetoMass`, `ksVetoWindow`, `ksVetoPionMass`). When enabled, a candidate whose two daughter tracks evaluated under the (pi+, pi-) hypothesis fall within +/- ksVetoWindow of the K_S PDG mass is rejected before the kinematic fit. Default off so the K_S producer itself is unaffected; turned on in the Lambda cfi (+/-10 MeV window) to suppress real K_S contamination at low Lambda pT, where the wrong-mass-hypothesis K_S mass falls inside the Lambda window. LambdaToProtonPi cfi: also bump daughter masses to PDG precision (0.93827208943 / 0.13957039 with their PDG uncertainties). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Geant4ePropagator: add an optional 8th argument `particleNameOverride` to propagateGenericWithJacobianAltD. When non-empty it is used directly in the G4ErrorFreeTrajState constructor instead of the propagator's constructor-bound `theParticleName` (default "mu"). Empty string preserves the existing muon behaviour for J/psi / Upsilon callers. Also adds three per-failure-point counters (`plimit`, `ierr`, `maxlen`) and detailed kinematic prints at each failure exit -- used to diagnose the Geant4e propagation-failure rate on V0 daughters. The destructor prints a one-line summary of total calls and per-mode failure counts. G4ErrorPhysicsListCustom: register K+/-, anti-proton particle definitions in addition to the existing gamma / e+/- / mu+/- / pi+/- / proton. Without these, G4ErrorTrajState::BuildCharge() aborts with a fatal G4Exception when asked to propagate a kaon / anti-proton (D0 and Lambda-bar daughters). ResidualGlobalCorrectionMakerTwoTrackG4e: read per-daughter G4 base particle names from cfi (`daughterParticleName1/2`), construct the full G4 particle name per track from (base, charge) -- "<base>+"/"-" for mesons/leptons, "proton"/"anti_proton" for the baryon case -- and pass it to propagateGenericWithJacobianAltD(). Empty cfi -> empty override -> propagator falls back to muon (back-compatible with existing J/psi/Upsilon runners). V0 channel cfis (PiPi, ProtonPi, KPi): set daughterParticleName1/2 to "pi"/"pi", "proton"/"pi", "kaon"/"pi". Bump daughter masses and errors to PDG values, and correct the previous massConstraintWidth values from "lifetime in ms" to "natural width in GeV" (Gamma = hbar/tau): K_S 7.351e-15, Lambda 2.502e-15, D0 1.605e-12. V0 runner cfgs (KS, Lambda, D0): override the global Geant4ePropagator PropagationPtotLimit from the default 0.5 GeV down to 0.05 GeV. The 0.5 GeV threshold was tuned for muon-spectrometer extrapolation but rejects ~70% of V0-daughter pion / soft-proton tracks (lab |p| can fall below 0.5 GeV at low Lambda pT or for backward-emitted daughters). On a 500-event Lambda quickcheck this drops the per-call propagation failure rate from ~190 to ~70 per 500 events (~64% reduction). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds Analysis/HitAnalyzer/{interface,plugins}/ParticleProperties.{h,cc}
with two free functions in namespace ana_hitanalyzer:
ParticleProperties getParticleProperties(const std::string &base);
returns {mass, massErr} (GeV) for the given particle base name.
Recognised: "mu", "pi", "kaon", "proton", "e".
std::string g4ParticleName(const std::string &base, int charge);
formats the G4 particle name for the propagator override:
empty base -> empty (caller falls back to propagator default)
"proton" -> "proton" / "anti_proton"
everything else -> base + "+" / "-".
All four producers / ntuplizers in HitAnalyzer that previously held
their own copies of these constants now look them up from the single
table:
V0CandidateProducer : daughter mass + err from
daughterParticleName1/2
DstToD0PiCandidateProducer : kaon, pion, soft-pion masses hard-coded
by name (this channel is K + pi + pi)
ResidualGlobalCorrectionMakerG4e (single-track):
trackMass from trackParticleName, plus
per-track G4-name override on every
propagator call (mu/pi/kaon/proton).
ResidualGlobalCorrectionMakerTwoTrackG4e:
daughterMass1/2 + err from
daughterParticleName1/2.
The base class no longer carries g4ParticleName as a static helper --
it lives in the shared utility instead, so V0CandidateProducer (which
is not in the CVH inheritance hierarchy) can use the same
implementation.
Channel cfis simplified accordingly: removed the redundant numeric
daughterMass1/2 and daughterMass1/2Err (and kaonMass / pionMass /
softPionMass for D*) cms.double() entries -- they are now derived
from the particle name in one place. Updating a particle's PDG mass
becomes a one-line change in ParticleProperties.cc rather than a
scatter of cms.double()s across half a dozen cfis.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Stage-1 ALCAReco now persists *Resonances VertexCompositeCandidateCollection
collections for KS, Lambda, J/psi, Upsilon, Z (via the new stage-1
VertexCompositeCandidateRemapper). Switch the CVH refit to consume them
directly: one tree row is emitted per persisted candidate, no
re-pairing of tracks at stage-2.
ResidualGlobalCorrectionMakerBase + ResidualGlobalCorrectionMakerTwoTrackG4e:
- Add an optional `srcCandidates` InputTag (default empty) and an
inputCandidates_ token in the base class, registered only when the
label is non-empty.
- At the top of produce(), build a vector of (Track*, Track*) pairs:
if srcCandidates is set, iterate the persisted candidates and use
cand.daughter(0/1)->track() refs; otherwise fall back to the legacy
j>i outer-product over the input TrackCollection.
- Replace the existing nested loop with a single flat loop over the
pair vector; per-pair body unchanged.
- Tighten the dE/dx parameter-existence check to require non-empty
InputTag labels (existsAs<>() passes for empty InputTag('') but
getByToken would then throw ProductNotFound at runtime).
- BuildFile: -Wno-error -Wno-unused-variable to keep pre-existing
unused-variable warnings from failing the build on full recompile.
cfis:
- New ResidualGlobalCorrectionMakerTwoTrack{JpsiMuMu,UpsilonMuMu,ZMuMu}G4e_cfi
configurations targeting the persisted *Resonances candidates.
- ResidualGlobalCorrectionMakerTwoTrack{PiPi,ProtonPi,KPi}G4e_cfi: default
srcCandidates set to ALCARECOTkAl{KsToPiPi,LambdaToProtonPi,DstToD0Pi}Resonances.
Removed V0CandidateProducer.cc and its KsToPiPiCandidateProducer_cfi /
LambdaToProtonPiCandidateProducer_cfi -- all V0 ALCAReco now publishes
the *Resonances candidates, no legacy samples need stage-2 re-pairing.
runGlobalCorRec{Ks,Lambda}FromAlca.py simplified to drop the
candidate-producer step (CVH module reads the candidates from the cfi
default).
Adds Charmonium-flavored runner copies (runCvhJpsiCandidateDriven{,100files}.py,
runGlobalCorRec{Ks,Lambda}From{CharmoniumAlca,Charmonium100files}.py) for
running the new pipeline end-to-end on the Charmonium-derived ALCAReco
that the stage-1 work now produces.
Test plan:
- 1-file Charmonium: 706 J/psi candidates -> 702 entries refit (4 dropped
by isLooper skip), 0/82329 Geant4e propagation failures, J/psi mass =
3.093 +/- 0.068 GeV.
- 100-file Charmonium (merged via edmCopyPickMerge): 9-variant scan
(KS / Lambda / J/psi x nv_np / v_np / v_p) completed with 0 failures.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…4e path limit 1. cmsRun crashed in TTree::AutoSave at ~3 k events with fillGrads=true, the trace ending in GenericWriteAction inside the tree-metadata streamer. SetAutoSave(0) disables the in-job metadata save (the tree is still written at Close), eliminating that crash path. 2. Slow scalar baskets never flushed during the run, so peak RSS reached 3.4 GB after one hour at 100 kB/event. SetAutoFlush(-100 MB) flushes all open baskets every 100 MB accumulated, capping peak near 1.5 GB even on 800 k-event jobs. 3. Geant4ePropagator's max-path-length cutoff had been tightened from 10 m to 200 cm for V0 / mixed-particle debugging; reverting so long J/psi muon trajectories are no longer truncated at 200 cm. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reads ALCARECOTkAlJpsiMuMu directly (no candidate producer required; relies on the legacy in-module pair loop when srcCandidates is unset). Knobs (VarParsing 'analysis', all optional with sensible defaults): input local path or root:// URL of one ALCARECO file nEvents -1 = all fillJac per-track Jacobians (default true) fillGrads per-event gradient + packed Hessian (default false) doMassConstraint apply J/psi mass constraint (default false) useIdealGeometry default true; pass 0 to use real alignment goldenJson optional path; applied at the source as lumisToProcess Also installs an HLTHighLevel pre-filter using the same J/psi paths the producer records decisions for, so events that fired none are skipped before geopro/CVH. Driven by calibration_studies/slurm/submit.sh via --config and --cmsrun-args. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…s runs Root cause of the OOM kills observed in production array 5163621 (~35 % SIGSEGV failures with malloc-time crashes when MaxRSS hit the 4 GB cgroup cap): SetAutoFlush(-100 MB) does NOT mean "flush every 100 MB" in ROOT 6.14. It converts the byte threshold to a fixed entry count at first fill, based on the size of the first event. For our tree the first fill is ~80 kB so ROOT recorded fAutoFlush = 1248 entries. Later events have a much heavier hesspackedv (up to ~150 kB), so 1248 entries already buffer 100-200 MB of basket data per cluster. With ~100 branches each holding their open basket, peak basket memory drifts toward ~400 MB on top of the ~1.5 GB CMSSW baseline -- and the slowest-filling branches accumulate even more between AutoFlush boundaries. Fix: SetAutoFlush(200). Predictable 200-entry clusters; per-cluster basket footprint ~20 MB regardless of per-event size. Validated on a 30 k-event smoke (j5164555): fAutoFlush stored as 200 (was 1248) MaxRSS 2.25 GB at 30 k events (was 2.74 GB at 20 k events) bounded growth, fits in --mem 4G with comfortable headroom Wall time slightly improves (~0.097 s/event vs 0.13 s/event) because small clusters stall less on flush. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds step-2 ntuplizers for V0/D* alignment-calibration channels and a 2D-transverse pointing constraint to the unified two-track CVH refit. The five commits build on each other:
ffd5c30 — Jpsi and Upsilon cfg after clean up— small config tidy-up of the existing J/psi and Upsilon runners.2008e5e — preliminary ntuplizer for D0 with custom track fit(emanca) — addsDstToD0PiCandidateProducer(D* -> D0 pi candidate finder) and a dedicatedResidualGlobalCorrectionMakerTwoTrackKPiG4eclass for the asymmetric K/pi pair.f2c0c7f — Add KS and Lambda step-2 ntuplizers (V0CandidateProducer + CVH refit)— genericV0CandidateProducer(configurable daughter masses, optional both-assignment swap for asymmetric V0s) plus channel cfis and runners forKS -> pi+pi-andLambda -> p pi-.800021d — Unify TwoTrackG4e classes and split V0 candidate cfis per channel— collapses the duplicateTwoTrackG4e/TwoTrackKPiG4eclasses into one configurable class (per-daughter masses,respectTrackOrder, optional L1 trigger), saving ~2700 lines. Per-channel cfis (PiPi, ProtonPi, KPi) configure the same C++.d6c9cf0 — Add optional 2D-transverse pointing constraint to TwoTrackG4e— new optionaldoPointingConstraint/pointingSigmacfi parameters. Constraint requires the V0 transverse momentum to coincide with the BS->SV flight vector. One scalar chi^2 term, +1 ndof; default off so legacy J/psi/Upsilon/D* runners are unchanged.Validation (data, KS pointing OFF -> ON):
Net diff: +1570 lines / -31 lines across 15 files in
Analysis/HitAnalyzer/. No changes outsideAnalysis/HitAnalyzer/. The parallel magnet-field PR #42 has been intentionally rebased out of this branch; the two efforts are orthogonal.Test plan
scram b -j8succeeds with no warnings on a fresh checkout.doPointingConstraint = True, KS / Lambda mass RMS narrows slightly; convergence stays within 1-2 extra iterations.Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com