Repository navigation
TPC SCD: round instead of truncate when packing the unbinned residuals - #16046
Conversation
UnbinnedResid packed y, z, dy, dz and tgSlp into shorts with a plain cast, i.e. truncated towards zero: every value moved towards zero by up to one step (15 um for y, 6 um for dy). The decoded cluster position y + dy then depends on the sign of dy below the step size, and since the cluster positions lie on the ClusterNative 1/64-pad grid, binning residuals by the cluster position on sub-pad scales splits the clusters of a grid point by the sign of dy: a comb of up to +-0.8 mm in the mean residual (seen identically in PbPb data and no-distortion MC; snapping the decoded position back to the 1/64-pad grid removes it). Pack with rounding instead (UnbinnedResid::pack); unclamped tgSlp stays at |tgSlp| <= 0x7fff - 1, so isTgSlpClamped() is unchanged. Same rounding for the vertex x and the TRD tracklet slope. No format change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| /// y + dy then depends on the sign of dy below the step size, which biases residuals binned by the cluster position on sub-pad scales | ||
| static short pack(float value, float maximum, int limit = 0x7fff) | ||
| { | ||
| return static_cast<short>(std::clamp<long>(std::lround(value * 0x7fff / maximum), -limit, limit)); |
There was a problem hiding this comment.
Better do this as
return static_cast<short>(std::clamp<long>(std::lround(value * limit / maximum), -limit, limit));
There was a problem hiding this comment.
For tgSlp the limit is std::abs(tgSlpIn) < param::MaxTgSlp ? 0x7fff - 1 : 0x7fff (unclamped values must not round onto 0x7fff, which isTgSlpClamped() uses as the clamp marker). With limit as the scale, unclamped tgSlp would be packed with 0x7ffe while the decoding uses 0x7fff, a relative difference of 3e-5. Negligible in practice, but packing and decoding would no longer use the same scale. Shall I still change it or keep 0x7fff as the scale and limit only for the clipping?
There was a problem hiding this comment.
Ah, indeed. I just find it confusing that the pack() looks generic, but the limit is intrinsically tied to the scale constant. Perhaps one should simply rename pack to packToUShort
There was a problem hiding this comment.
Yes, I agree. I renamed it, thanks!
…d 0x7fff scale Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UnbinnedResidpackedy,z,dy,dzandtgSlpinto shorts with a plain cast, i.e. truncated towards zero. Every value moves towards zero by up to one step (15.3 µm fory, 6.1 µm fordy), so the decoded cluster positiony + dydepends on the sign ofdybelow the step size. The cluster positions lie on the ClusterNative 1/64-pad grid; binning residuals by the cluster position on sub-pad scales therefore splits the clusters of a grid point by the sign ofdy, giving a comb of up to ±0.8 mm in the mean residual. It appears identically in Pb-Pb data and in MC without distortions; snapping the decoded position back to the 1/64-pad grid removes it, and the snapped MC agrees with the MC-truth reference.Change:
UnbinnedResid::pack()rounds to the nearest step. UnclampedtgSlpstays at |tgSlp| ≤ 0x7fff − 1, soisTgSlpClamped()is unchanged. Same rounding for the vertex x and the TRD tracklet slope. No format change.Effect on the maps: negligible; voxels are far coarser than the packing step. The fix matters for analyses that bin by the cluster position on sub-pad scales (pad-response / sector-edge studies). Existing productions can be corrected in the analysis by snapping
y + dyto the 1/64-pad grid (the packing error, ≤ 21 µm, is below half a grid step).Mean TPC residual vs the pad phase of the decoded cluster position (40 bins per pad); grey: as decoded, colour: snapped to the 1/64-pad grid, dashed: MC truth. Clusters without split / single / shared flags, > 3.5 pads from the sector edge, |tan φ| < 0.3.
🤖 Generated with Claude Code