Skip to content

Commit be7148b

Browse files
[Common] Add legacy fallback for TPC side detection without side flags
The TPCSideA/TPCSideC flags were only added to the AOD format in February 2026, so datasets produced before that always have neither bit set. Detect CE-crossing tracks from the track's asymmetric time margins and otherwise infer the side from a cross-check of Z and tgl sign, per TPC domain expert guidance, instead of silently skipping the correction for all tracks in older datasets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent ea25e43 commit be7148b

1 file changed

Lines changed: 44 additions & 2 deletions

File tree

Common/Core/TPCVDriftManager.h

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,48 @@ class TPCVDriftManager
132132

133133
// impose new Z coordinate
134134
const auto sides = (trackExtra.flags() & (o2::aod::track::TrackFlags::TPCSideA | o2::aod::track::TrackFlags::TPCSideC));
135-
track.setZ(track.getZ() + (sides == o2::aod::track::TrackFlags::TPCSideC ? -dDrift : (sides == o2::aod::track::TrackFlags::TPCSideA ? dDrift : 0)));
135+
float zShift = 0.f;
136+
if (sides == o2::aod::track::TrackFlags::TPCSideA) {
137+
zShift = dDrift;
138+
} else if (sides == o2::aod::track::TrackFlags::TPCSideC) {
139+
zShift = -dDrift;
140+
} else if (sides == 0) {
141+
// Fallback for datasets produced before the TPC side flags were introduced (Feb. 2026).
142+
o2::aod::track::extensions::TPCTimeErrEncoding tEnc;
143+
tEnc.encoding.timeErr = trackExtra.trackTimeRes();
144+
const float dFwd = tEnc.getDeltaTFwd();
145+
const float dBwd = tEnc.getDeltaTBwd();
146+
// Equal, small forward/backward margins mean the track is bounded on both ends,
147+
// i.e. it crosses the CE: it cannot be moved and is already corrected elsewhere.
148+
const bool crossesCE = (dFwd == dBwd) && (dFwd < mMaxCECrossingDeltaTNS);
149+
if (!crossesCE) {
150+
const bool zPositive = track.getZ() > 0.f;
151+
const bool tglPositive = track.getTgl() > 0.f;
152+
int side = 0; // +1 = A, -1 = C, 0 = undetermined -> leave uncorrected
153+
if (zPositive == tglPositive) {
154+
// Consistent sign: the track converges to Z=0 at the beamline by construction.
155+
side = tglPositive ? 1 : -1;
156+
} else if (dBwd == 0.f && dFwd > 0.f) {
157+
// Bounded backward at the CE with room forward: no clusters on the opposite
158+
// side, so trust the measured Z rather than the (here inverted) tgl.
159+
side = zPositive ? 1 : -1;
160+
} else if (dBwd > 0.f) {
161+
// Large tgl track bounded at the readout side instead: trust tgl.
162+
side = tglPositive ? 1 : -1;
163+
}
164+
// else: degenerate case, track touches both CE and readout -> cannot be deduced/moved.
165+
// (in practice unreachable here: dFwd==dBwd==0 would already satisfy crossesCE above,
166+
// since dFwd/dBwd are always >= 0; kept explicit to mirror the reference logic 1:1.)
167+
168+
if (side > 0) {
169+
zShift = dDrift;
170+
} else if (side < 0) {
171+
zShift = -dDrift;
172+
}
173+
}
174+
}
175+
// else: track has clusters on both sides (crossed the CE) and is already corrected elsewhere
176+
track.setZ(track.getZ() + zShift);
136177
if constexpr (std::is_base_of_v<o2::track::TrackParCov, Track>) {
137178
track.setCov(track.getSigmaZ2() + dDriftErr * dDriftErr, o2::track::kSigZ2);
138179
}
@@ -157,7 +198,8 @@ class TPCVDriftManager
157198
o2::ccdb::BasicCCDBManager* mCCDB{}; // reference to initialized ccdb manager
158199

159200
static constexpr unsigned int mWarningLimit{10};
160-
static constexpr float mMaxDriftCm{250.f}; // TPC drift volume half-length in cm
201+
static constexpr float mMaxDriftCm{250.f}; // TPC drift volume half-length in cm
202+
static constexpr float mMaxCECrossingDeltaTNS{1000.f}; // ~1 us, ballpark forward/backward time margin of a CE-crossing track
161203

162204
// Counters
163205
unsigned int mCalls{0}; // total number of calls

0 commit comments

Comments
 (0)