Skip to content

TPC: Improve standalone dEdx calculation class - #15650

Open
tubagundem wants to merge 9 commits into
AliceO2Group:devfrom
tubagundem:TPC_dEdx_calculation
Open

tubagundem wants to merge 9 commits into
AliceO2Group:devfrom
tubagundem:TPC_dEdx_calculation

Conversation

@tubagundem

@tubagundem tubagundem commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Changes

  • Merge same-row clusters (redesigned into a configurable, proximity-gated method), then fix the traversal bug the original merge exposed:
    The first two commits add handleSameRowClusters(), which unconditionally merged every cluster falling in the same (sector, row) and assigned to the same track into one combined cluster; pad/time a charge-weighted average, total charge summed, max charge the max across the group; and fixed a traversal bug this merge exposed by walking clusters in the track's actual native cluster-reference order (rowOrder, recorded on first encounter) instead of assuming a fixed row order. This was later redesigned: a same-row group is now only eligible for merging if it passes a pad/time proximity gate (setSameRowMaxPadDiff()/setSameRowMaxTimeDiff(), default 3 pad / 4 time bins); a group that fails it (e.g. the two legs of a low-p looper crossing the same row) is always kept as one independent sample per cluster, since merging those was biasing the low-p OROC3 dE/dx tail. For groups that do pass the gate, the new dEdxSettings::sameRowClusterMethod selects how: 0 = don't merge (one sample per raw cluster), 1 = merge and sum (the original charge-weighted-average/sum behavior), 2 (default) = merge by taking the single dominant (largest qTot) fragment.

  • Fixed frequent track-propagation failures, plus a faster propagation option:
    Track propagation (mPropagateTrack) previously left the track state corrupted if rotate() or PropagateToXBxByBz failed partway through a step, causing every later row's propagation to fail too. Both now snapshot the track state first and roll back to it on failure, so one bad row no longer poisons the rest. Also added setPropagateParams() / mPropagateParams, a cheaper alternative that propagates only the track parameters (no material corrections) instead of the full track.

  • Refit once per track, then propagate to each cluster row:
    In refit mode CalculatedEdx used to call RefitTrackAsGPU() once per cluster row (with setTrackReferenceX(rowX)). That refits the whole track every time, so for a low-p track whose fitted helix falls short of an outer row, every row further out failed in the same way and the track was left at the wrong X. It now calls RefitTrackAsGPU(track, outward=true, resetCov=true) once per track (refitTrack()) and obtains the state at each row with the new propagateTrackToX() (rotate + PropagateToXBxByBz with the same fallback ladder as mPropagateTrack/mPropagateParams: material LUT, then no material, then parameter-only). If the refit fails (negative return or NaN parameter), the track is restored to its pre-refit state and all of its rows are propagated from there.

  • Debug-streamer additions in GPU/ (compiled only with DEBUG_STREAMER):
    GPUdEdx::fillCluster() gets the extra iTrk/flags arguments and the raw clqTot/clqMax only inside GPUCA_DEBUG_STREAMER_CHECK(...), and tree_dedx writes them; GPUTPCGMO2Output.cxx writes a tree_indices stream (trackID in the output array vs. internal merged-track index) inside the same macro. This lets the tracker's per-cluster dE/dx rows be matched to the persisted tracks.

  • Excluded clusters no longer bias the subthreshold fill:
    Minimum-charge tracking and missing-cluster counting used to run over every cluster, including ones that would later be excluded (dead region, edge, failed propagation, etc.). Now they only run over clusters that are actually accepted into the dE/dx calculation.

  • New cluster-exclusion:

    • ExcludeSplitCl split into ExcludeSplitPadCl and ExcludeSplitTimeCl for finer-grained control (bit values renumbered).
    • ExcludeSamePadRowCl — exclude clusters that were merged from the same pad row.
    • Stack-boundary exclusion via new isInStackBoundaries() and a mStackBoundaries table (row ranges per GEM stack), controlled by a new stackBoundaryMethod parameter (0 = disabled, 1 = exclude boundary row, 2 = also exclude the adjacent row).
    • Dead-channel exclusion: a dead channel map is now loaded from CCDB (loadCalibsFromCCDB()) and clusters in dead regions are excluded.
  • Per-region occupancy output:
    Added an AverageOccupancy struct (IROC/OROC1-3) and a new output parameter on calculatedEdx() returning average track occupancy per TPC region.

  • Debug streamer overhaul:
    Replaced the old per-track parallel debug vectors with two linked trees: dEdxDebugTrack (one row per track: pristine track, output, average occupancy, summary counts) and dEdxDebugCl (one row per cluster, with the track's propagated/refit parameters at that cluster). Rows are matched via a new running mDebugTrackIndex. setStreamer() also now keeps a map of streamers keyed by output filename, so different calculatedEdx() calls writing to different debug files each get their own independent tree.

  • Simplified subthreshold cluster filling:
    Removed the Landau-based method (method=2) from fillMissingClusters(), keeping only minimum-charge and minimum-charge/2.

  • getOccupancy() signature change:
    Now takes a cluster time (float) instead of a ClusterNative, and is only valid when the refit method (not propagation) is used, since the occupancy map is only filled by setRefit().

  • Evaluate multiple dE/dx settings per track without repeating propagation:
    New dEdxSettings struct bundles a full configuration (truncation range, correction mask, cluster mask, subthreshold/stack-boundary/same-row-cluster method, per-call debug file, subthreshold charge caps). Per-track work is split into gatherRowClusterData() (refit/propagation, run once per track) and calculatedEdxFromRowData() (settings-dependent charge computation, run once per dEdxSettings entry against the shared row data). New calculatedEdxMultipleSettings() entry point ties the two together and returns one dEdxInfo per settings entry.

  • Legacy calculatedEdx() no longer duplicates the implementation:
    The standalone single-settings calculatedEdx() overloads are now thin wrappers that build a dEdxSettings and delegate to the shared gatherRowClusterData()/calculatedEdxFromRowData() path used by calculatedEdxMultipleSettings().

  • Subthreshold charge cap reintroduced as a per-region value clamp instead of an all-or-nothing gate:
    The old mMinChargeTotThreshold/mMinChargeMaxThreshold gated whether a gap got filled at all, track-wide across all regions: if the track's weakest accepted cluster anywhere exceeded the threshold, no gap on that track was filled in any region, undercounting subthreshold clusters relative to online tracking. Removed those members entirely; subthreshold gaps are now always filled (unless excluded), using a per-region running minimum charge, optionally clamped per settings entry via new dEdxSettings::maxSubthresholdChargeTot/maxSubthresholdChargeMax (default effectively uncapped) applied only to the fill value, never to whether/how many gaps get filled. fillMissingClusters() now takes per-region (float[4]) minimum-charge arrays instead of one track-wide scalar.

  • Fixed a correction-map crash/inaccuracy from mutating an already-flattened transform in place:
    CalculatedEdx built its refit correction map as a TPCFastTransformPOD and then mutated that same flattened buffer directly (setTPCCorrMap()/vDrift updates); not a supported use of the POD form, and a source of heap corruption under production load. Fixed by keeping a regular, exclusively-owned TPCFastTransform (mTPCCorrMapFull) as the only object ever mutated, and re-flattening it into the POD buffer the GPU refitter reads via a new rebuildTPCCorrMapPOD() after every change. Along the way, loadCalibsFromCCDB() (and the matching local-snapshot loader) gained a loadVDriftForRefit option (default on) that fetches TPC/Calib/VDriftTgl and applies it to the refit transform via new setTPCVDrift()/setVDriftFromFile(); previously the refit transform carried no calibrated drift velocity/t0 at all, biasing the local z coordinates the refit sees.

  • New debug/monitoring counters:

    • getNPropagationFailed()/getNRowsProcessed() track refit/propagation failure rate.
    • getNRefitFallback() counts the rows of tracks whose refit failed and which were therefore propagated from their original state.
    • getNSubThresholdFilledPerSettings() tracks subthreshold fills per dEdxSettings entry.
    • resetDebugCounters() resets all of them.
  • Added macro/calculatedEdx.C:
    Example macro demonstrating standalone use of CalculatedEdx: reads TPC tracks and native clusters (data or MC), optionally restricting to TPC tracks matched to an ITS track, driven by a single settingsList vector parameter so multiple dE/dx settings can be evaluated per track in one run. The output tree gains one dEdx<i> branch per settingsList entry (instead of a single dEdx branch). Processes each event's tracks sequentially through a single CalculatedEdx instance; debug streamer files are now named per-setting only (dEdxDebug_s<j>.root) when more than one setting is requested. Also adds a maxEvents parameter to cap the number of processed events, options to feed space-charge/drift-velocity calibration into the refit transform, and per-event logging of the refit/propagation failure rate and subthreshold-fill counts broken down per settings entry.

@shahor02

Copy link
Copy Markdown
Collaborator

Is this relevant for the pb26 production?

@tubagundem

Copy link
Copy Markdown
Contributor Author

Is this relevant for the pb26 production?

No, this is just to improve the dE/dx calculation class if we want to use it later or if we want to debug things locally.

…n calibration when

isMC is set, remove MC kinematics reader from dEdx calculation macro
…ck without repeating track propagation and introduced minor improvements
Comment thread Detectors/TPC/calibration/include/TPCCalibration/CalculatedEdx.h
… Nico's changes to dEdx calcualtion class, minor improvements
@tubagundem
tubagundem marked this pull request as ready for review August 14, 2026 12:39
@tubagundem
tubagundem marked this pull request as draft September 4, 2026 11:52
… style

outer-row cut, align correction order and sin^2(phi) cap with online GPUdEdx
…ference()/GPUTrackingRefit::RefitTrack() report whether they reached the reference X) so CalculatedEdx recovers rows where RefitTrackAsGPU() stalls, redesign same-row cluster handling into a configurable dEdxSettings::sameRowClusterMethod with a pad/time proximity gate, remove the duplicated legacy calculatedEdx() implementation in favor of the shared two-pass gatherRowClusterData()/calculatedEdxFromRowData() path, and simplify calculatedEdx.C into a single-threaded usage example
@tubagundem
tubagundem marked this pull request as ready for review September 18, 2026 13:13
@alibuild

alibuild commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Error while checking build/O2/fullCI_slc9 for d990619 at 2026-09-19 02:19:

## sw/BUILD/lhapdf-latest/log
collect2: error: ld returned 1 exit status


## sw/BUILD/VecGeom-latest/log
CMake Error at /container/bits/sw/BUILD/4b902089e8ae3e1235b16cffffe5b41003a490dd/VecGeom/buildExternals/VecCore-0.8.0/build/external/stamp/VecCore-0.8.0-configure-RELWITHDEBINFO.cmake:49 (message):
ninja: build stopped: subcommand failed.
CMake Error at cmake/modules/BuiltinVecCore.cmake:36 (add_custom_command):
CMake Error at CMakeLists.txt:222 (find_package):

Full log here.

Comment thread GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx Outdated
Comment thread GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx
Comment thread GPU/GPUTracking/dEdx/GPUdEdx.h
Comment thread GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx
Comment thread GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h
Comment thread GPU/GPUTracking/Refit/GPUTrackingRefit.cxx Outdated

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants