k4PFHitML is the key4hep/Gaudi inference implementation of the hit-based
particle-flow algorithm described in
arXiv:2603.04084. The standalone Python
reference implementation used for training (and for inference validation
against this package) lives at
github.com/doloresgarcia/HitPF.
The package handles data preparation from EDM4hep input collections, runs the
trained models (converted to ONNX) via k4FWCore::MultiTransformer, and
writes out particle-flow objects as standard EDM4hep collections.
This package targets the CLD detector concept and geometry: input
collection names (ECALBarrel, ECALEndcap, HCALBarrel, HCALEndcap,
HCALOther, MUON, SiTracks_Refitted, see the KeyValues in
PFHitML.cpp), the truth-matching barrel/endcap geometry constants
(truth_barrel_radius, truth_n_barrel_sides, truth_endcap_z), and the
models themselves.
Reconstruction proceeds in two model stages per event:
- Condensation/clustering model -- embeds every calorimeter hit and
track into a learned space and predicts, per hit/track, a condensation
point/beta value. Density Peak Clustering (DPC) on that embedding groups
hits and tracks belonging to the same particle into a
Shower(Clustering.h/.cpp,ShowerBuilder.h/.cpp). The DPC algorithm itself is a reimplementation of pgoltstein/densitypeakclustering (ρ local density, δ distance-to-higher-density, core/halo cluster assignment). - Regression/PID model -- per shower (charged and neutral run through
separate models), regresses energy and classifies particle type
(electron / charged hadron / neutral hadron / photon / muon)
(
PFParticleBuilder.h/.cpp).
Training data/samples:
- Both models are trained on CLD full simulation.
- The condensation model is trained on a Z→uds sample.
- The regression model is trained on a jet-like particle gun sample.
Truth matching is done via IoU (intersection-over-union) between a
reconstructed shower's constituent hits/tracks and each MCParticle's,
resolved with Hungarian matching (TruthMatcher.h). This produces the
HitPFMCTruthLink output collection, and is what any downstream fake-rate /
efficiency definition should be built on (a reconstructed particle with no
truth match is a fake; a truth particle with no reconstructed match is
missed).
HitPF(ReconstructedParticleCollection): the reconstructed particle-flow objects -- momentum, energy, mass, charge, PDG, reference point.HitPFIDs(ParticleIDCollection): PID hypothesis per particle (type, likelihood, PDG, full per-class score vector).HitPFMCTruthLink(RecoMCParticleLinkCollection): reco-to-truth links from the IoU matching above, weight = IoU.HitPFUnassociatedTracks(TrackCollection, opt-in): tracks that never ended up in any shower (DPC noise, or removed by the E/p consistency cut), so never made it onto anyHitPFparticle -- see below.
-
ROOT
-
PODIO
-
Gaudi
-
k4FWCore
Run, from the k4PFHitML directory:
cd /your/path/to/this/repo/k4PFHitML/
source ./setup.sh
k4_local_repo
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=../install -G Ninja -DPython_EXECUTABLE=$(which python3)
ninja installAlternatively you can source the nightlies instead of the releases:
source /cvmfs/sw-nightlies.hsf.org/key4hep/setup.shk4run k4PF-HitML/options/PerformMLPF.py --inputFiles <input.edm4hep.root> --outputFile output_HitPF.edm4hep.rootPerformMLPF.py also accepts all the configurable parameters listed below
(--dpc_d_c, --bFieldTesla, --write_unassociated_tracks, etc. --
run with --help for the full list).
The output is a ROOT file containing the input collections (keep *) plus
the HitPF/HitPFIDs/HitPFMCTruthLink (and, if enabled,
HitPFUnassociatedTracks) collections described above.
PFHitML.cpp-- the main GaudiMultiTransformeralgorithm; orchestrates the full per-event pipeline (data prep -> clustering ONNX -> DPC -> shower building -> regression ONNX -> particle assembly) and declares all configurableGaudi::Propertys.DataPreprocessing.h/.cpp-- builds the clustering model's input graph (calorimeter hits and tracks as nodes), and prepares per-shower inputs for the regression model.Clustering.h/.cpp-- Density Peak Clustering (DPC) on the clustering model's embedding output, plus the post-clustering E/p consistency cut (remove_bad_tracks_from_cluster) that can evict a track from its cluster.ShowerBuilder.h/.cpp-- turns DPC cluster labels intoShowerobjects, assigning each hit/track node back to its shower.Shower.h/.cpp-- per-shower data container (hits, tracks, betas) and shower-level derived quantities (mean track momentum, chi2, energy/momentum sums).PFParticleBuilder.h/.cpp-- turns aShowerplus regression/PID output into a finalHitPF/HitPFIDsentry (fillRecoParticle), including all the charged/neutral-specific conventions documented below.TruthMatcher.h-- IoU-based Hungarian matching between reconstructed showers and MCParticles; producesHitPFMCTruthLink.EvaluationSummary.h/.cpp-- collects per-event reco/truth summary rows (used for validation/debug logging, not part of the physics output).ONNXHelper.h/.cpp-- thin wrapper around ONNX Runtime for loading a model and running named inference.Helpers.h/.cpp-- shared small utility functions used across the above.
All exposed as Gaudi::Propertys on PFHitML, settable from
options/PerformMLPF.py:
- The three ONNX model paths (
model_path_clustering,model_path_properties_neutral,model_path_properties_charged----onnx_model_clustering/--onnx_model_properties_neutral/--onnx_model_properties_chargedon the command line). - Density Peak Clustering parameters (
dpc_d_c,dpc_rho_min,dpc_delta_min,dpc_core_radius). - Truth matching parameters (
truth_iou_threshold,truth_barrel_radius,truth_n_barrel_sides,truth_endcap_z). - Magnetic field strength (
bFieldTesla), used for track pT reconstruction from curvature. reassign_low_p_muons/muon_to_charged_hadron_p_threshold: reassigns a charged candidate predicted as muon below a momentum threshold to charged hadron instead (on by default) -- a punch-through/misidentified hadron is more likely than a genuine muon at that momentum.writeUnassociatedTracks: write tracks never assigned to any shower into theHitPFUnassociatedTracksoutput collection (off by default) -- e.g. for invariant-mass calculations that want to recover tracksHitPFitself drops (charged particles that curl up before reaching the calorimeter).
-
Reference point (
referencePoint): for charged particles this is the energy-weighted shower barycenter minus the driving track's calorimeter-entry position (PFParticleBuilder.cpp::buildChargedRecoInfo), matching Python'sPickPAtDCA.predict()(tools_for_regression.py:barycenters - p_xyz). For neutral particles,referencePointis instead an absolute shower-barycenter position (computeNeutralReferencePoint) -
Energy (
energy): for neutral particles this is a regression output. For charged particles it is the driving track's momentum magnitude|p|. -
Predicted class (
HitPFIDs.type): the canonical field for "which of the 5 classes did the model predict" isHitPFIDs'type(ParticleID.type, set viapid.setType(recoInfo.physicsClass)) --0=electron,1=charged hadron,2=neutral hadron,3=photon,4=muon. Use this, not PDG, to identify the predicted class: PDG (bothHitPF.PDGandHitPFIDs.PDG) is derived fromtypevia a fixed mass/species assumption per class (see below), not an independent measurement -- e.g. every charged hadron gets PDG ±211 (pion) regardless of whether it's actually a pion, kaon, or proton/ -
Mass / PDG: fixed per predicted class, not species-specific -- electron 0.511 MeV / PDG ±11, charged hadron assumed pion 139.57 MeV / PDG ±211, neutral hadron assumed neutron 939.57 MeV / PDG 2112, photon 0 / PDG 22, muon 105.658 MeV / PDG ±13 (
PFParticleBuilder.cpp::massFromPredictedClass,pdgFromChargedClass/pdgFromNeutralClass). Note that the PID classifier only differentiates e, gamma, CH, NH, mu. -
Charge: derived from the driving track's curvature sign (
chargeSignFromTrack,omega > 0-> +1), 0 for neutral particles. -
Tracks: only the single driving track (lowest chi2/ndf,
pickBestTrack) is attached to a chargedHitPFparticle'stracksrelation, even if DPC clustered more than one track into the same shower. Recover unassociated tracks via theHitPFUnassociatedTrackssubset collection (e.g. for invariant-mass calculations that want to include tracksHitPFitself drops). -
HitPFIDs.parameters: the full per-class softmax vector (not just the winning class), inchargedClassMap/neutralClassMaporder (PFHitML.cpp) -- 3 entries{e, CH, mu}for charged particles, 2 entries{NH, gamma}for neutral. Lets a downstream consumer define their own PID working point instead of only seeing the argmax decision already baked intophysicsClass/mass/PDG.
- arXiv:2603.04084 -- hit-based particle-flow algorithm this package implements.
- HitPF -- Python training/inference reference implementation.
- pgoltstein/densitypeakclustering
-- reference for the Density Peak Clustering algorithm reimplemented in
Clustering.h/.cpp.
Parts of this codebase and README were drafted with AI assistance, then reviewed and adapted if necessary.