Skip to content

Commit cc29423

Browse files
miranov25claude
andcommitted
TPC TimeSeries: optional TrackPar output (default off) and TRD readout flag
- tpcOut, trdIn and tpcAtVtx are written only with --store-full-trackpar (default off), as o2::track::TrackPar without covariance. The TPC/TRD propagation to the common local X = 275 cm plane, the TPC-TRD matching chi2 and the per-track TrackParCov copy in the TRD map run only with this option; otherwise tpcOutOK/trdInOK = 255 and the chi2 values are -1. With the option on, the three objects are the first branches of the tree. - New per-entry flag trdInReadout (TRD in detector readout from GRPECS: 1 yes, 0 no, 255 unknown). GRPECS is requested only when the unbinned output is enabled. - Deferred, not fixed: propagation targets local X (not lab radius), and the TRD starting state is the TrackTRD base state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YYeB7SW1TM7vU8qhVHiG61
1 parent 310a851 commit cc29423

1 file changed

Lines changed: 84 additions & 47 deletions

File tree

‎Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx‎

Lines changed: 84 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class TPCTimeSeries : public Task
7171
uint8_t trdPattern = 0;
7272
uint8_t nTRDTracklets = 0;
7373
int trackletIndices[6] = {-1, -1, -1, -1, -1, -1};
74-
o2::track::TrackParCov trdTrackParCov; // Phase 0.4: TRD track for propagation to R=275
74+
o2::track::TrackParCov trdTrackParCov; // TRD track state for propagation to the common local X = 275 cm plane; filled only with --store-full-trackpar
7575
bool hasTRDTrackPar = false;
7676
};
7777

@@ -90,9 +90,7 @@ class TPCTimeSeries : public Task
9090
float c11 = trk1.getSigmaSnp2() + trk2.getSigmaSnp2();
9191
float c12 = trk1.getSigma1PtSnp() + trk2.getSigma1PtSnp();
9292
float c22 = trk1.getSigma1Pt2() + trk2.getSigma1Pt2();
93-
float det = c00 * (c11 * c22 - c12 * c12)
94-
- c01 * (c01 * c22 - c12 * c02)
95-
+ c02 * (c01 * c12 - c11 * c02);
93+
float det = c00 * (c11 * c22 - c12 * c12) - c01 * (c01 * c22 - c12 * c02) + c02 * (c01 * c12 - c11 * c02);
9694
if (std::abs(det) < 1e-30f) {
9795
return -1.f;
9896
}
@@ -103,9 +101,7 @@ class TPCTimeSeries : public Task
103101
float i11 = (c00 * c22 - c02 * c02) * detInv;
104102
float i12 = (c01 * c02 - c00 * c12) * detInv;
105103
float i22 = (c00 * c11 - c01 * c01) * detInv;
106-
return d0 * (i00 * d0 + i01 * d1 + i02 * d2)
107-
+ d1 * (i01 * d0 + i11 * d1 + i12 * d2)
108-
+ d2 * (i02 * d0 + i12 * d1 + i22 * d2);
104+
return d0 * (i00 * d0 + i01 * d1 + i02 * d2) + d1 * (i01 * d0 + i11 * d1 + i12 * d2) + d2 * (i02 * d0 + i12 * d1 + i22 * d2);
109105
}
110106

111107
/// Chi2 match in non-bending plane (Z, Tgl) — 2 DOF
@@ -165,6 +161,7 @@ class TPCTimeSeries : public Task
165161
mXOuterMatching = ic.options().get<float>("refX-for-outer-ITS");
166162
mUseMinBiasTrigger = !ic.options().get<bool>("disable-min-bias-trigger");
167163
mMaxOccupancyHistBins = ic.options().get<int>("max-occupancy-bins");
164+
mStoreFullTrackPar = ic.options().get<bool>("store-full-trackpar");
168165

169166
if (mUnbinnedWriter) {
170167
for (int iThread = 0; iThread < mNThreads; ++iThread) {
@@ -191,6 +188,13 @@ class TPCTimeSeries : public Task
191188
void run(ProcessingContext& pc) final
192189
{
193190
o2::base::GRPGeomHelper::instance().checkUpdates(pc);
191+
// run-level TRD readout flag from GRPECS (unbinned output only): 1 = TRD in readout, 0 = not in readout,
192+
// 255 = unknown (defensive: GRPECS is requested only with the unbinned writer)
193+
mTRDInReadout = 255;
194+
if (mUnbinnedWriter) {
195+
const auto* grpecs = o2::base::GRPGeomHelper::instance().getGRPECS();
196+
mTRDInReadout = grpecs ? static_cast<uint8_t>(grpecs->isDetReadOut(DetID::TRD) ? 1 : 0) : static_cast<uint8_t>(255);
197+
}
194198
mTPCVDriftHelper.extractCCDBInputs(pc);
195199
mPTHelper.extractCCDBInputs(pc);
196200
if (mTPCVDriftHelper.isUpdated()) {
@@ -394,8 +398,10 @@ class TPCTimeSeries : public Task
394398
trdData.trackletIndices[iLay] = trkltId;
395399
}
396400
}
397-
trdData.trdTrackParCov = o2::track::TrackParCov(trdTrack);
398-
trdData.hasTRDTrackPar = true;
401+
if (mStoreFullTrackPar) {
402+
trdData.trdTrackParCov = o2::track::TrackParCov(trdTrack); // TrackTRD base state, not validated as the outer/entrance state (open Phase 0.4 finding)
403+
trdData.hasTRDTrackPar = true;
404+
}
399405
tpcToTRDMap[refTPC] = trdData;
400406
}
401407
}
@@ -1211,6 +1217,8 @@ class TPCTimeSeries : public Task
12111217
float mMaxdEdxRegionRatio{0.5}; ///< maximum abs dedx region ratio: log(dedx_region/dedx)
12121218
float mSamplingFactor{0.1}; ///< sampling factor in case sampling is used for unbinned data
12131219
bool mSampleTsallis{false}; ///< perform sampling of unbinned data
1220+
bool mStoreFullTrackPar{false}; ///< experimental: compute and store tpcOut/trdIn/tpcAtVtx TrackPar and TPC-TRD chi2 (constant per job)
1221+
uint8_t mTRDInReadout{255}; ///< TRD in detector readout (GRPECS): 1 yes, 0 no, 255 unknown
12141222
std::vector<std::mt19937> mGenerator; ///< random generator for debug tree sampling
12151223
std::vector<std::unique_ptr<o2::utils::TreeStreamRedirector>> mStreamer; ///< streamer for unbinned data
12161224
float mXOuterMatching{60}; ///< reference x where delta track parameters for TPC with ITS-TPC track are stored in skimmed data
@@ -1477,40 +1485,46 @@ class TPCTimeSeries : public Task
14771485
}
14781486
}
14791487

1480-
// Phase 0.4: TPC out / TRD in at kTPCTRDMiddle, same frame, same X
1481-
static constexpr float kTPCTRDMiddle = 275.f; // cm, midpoint TPC outer / TRD layer 0
1482-
auto propagator = o2::base::Propagator::Instance();
1483-
1484-
// D1: TPC outer → kTPCTRDMiddle
1485-
o2::track::TrackParCov tpcOutCov(tracksTPC[iTrk].getParamOut());
1486-
uint8_t tpcOutOK = 2; // default: propagation failed
1487-
{
1488-
// rotate to sector angle at current position
1489-
float sn = std::sin(tpcOutCov.getAlpha());
1490-
float cs = std::cos(tpcOutCov.getAlpha());
1491-
float gy = tpcOutCov.getX() * sn + tpcOutCov.getY() * cs;
1492-
float gx = tpcOutCov.getX() * cs - tpcOutCov.getY() * sn;
1493-
if (tpcOutCov.rotate(std::atan2(gy, gx)) &&
1494-
propagator->propagateTo(tpcOutCov, kTPCTRDMiddle, false, mMaxSnp, mCoarseStep, mMatType)) {
1495-
tpcOutOK = 0;
1496-
}
1497-
}
1498-
1499-
// D2: TRD matched → kTPCTRDMiddle, rotated to SAME alpha as TPC out
1488+
// Optional TPC-out / TRD-in states on a common local X plane (experimental, --store-full-trackpar, default off).
1489+
// Open Phase 0.4 findings, deferred: the target is local X = kTPCTRDMiddle in the frame rotated to the track
1490+
// position angle (lab radius = sqrt(X^2 + Y^2), not exactly 275 cm), and the TRD starting state is the TrackTRD
1491+
// base state, not validated as the outer/entrance state. With the switch off nothing below is computed.
1492+
static constexpr float kTPCTRDMiddle = 275.f; // cm, common local X plane between TPC outer wall and TRD layer 0
1493+
o2::track::TrackParCov tpcOutCov; // local temporary with covariance (needed for chi2); persisted as TrackPar
15001494
o2::track::TrackParCov trdInCov;
1501-
uint8_t trdInOK = 1; // default: no TRD match
1502-
float chi2PhiTPCTRD = -1.f;
1495+
uint8_t tpcOutOK = 255; // 0 propagated, 2 propagation failed, 255 switch off (not computed)
1496+
uint8_t trdInOK = 255; // 0 propagated, 1 not attempted (no TRD match or tpcOutOK != 0), 2 propagation failed, 255 switch off; count TRD matches with trdPattern > 0
1497+
float chi2PhiTPCTRD = -1.f; // -1: not computed (switch off, trdInOK != 0) or singular covariance
15031498
float chi2ZTPCTRD = -1.f;
1504-
if (itTRD != tpcToTRDMap.end() && itTRD->second.hasTRDTrackPar && tpcOutOK == 0) {
1505-
trdInCov = o2::track::TrackParCov(itTRD->second.trdTrackParCov);
1506-
// rotate to TPC alpha FIRST, then propagate — guarantees same frame + same X
1507-
if (trdInCov.rotate(tpcOutCov.getAlpha()) &&
1508-
propagator->propagateTo(trdInCov, kTPCTRDMiddle, false, mMaxSnp, mCoarseStep, mMatType)) {
1509-
trdInOK = 0;
1510-
chi2PhiTPCTRD = chi2Match_Phi(tpcOutCov, trdInCov);
1511-
chi2ZTPCTRD = chi2Match_Z(tpcOutCov, trdInCov);
1512-
} else {
1513-
trdInOK = 2; // propagation failed
1499+
if (mStoreFullTrackPar) {
1500+
// D1: TPC outer → kTPCTRDMiddle
1501+
tpcOutCov = o2::track::TrackParCov(tracksTPC[iTrk].getParamOut());
1502+
tpcOutOK = 2; // default: propagation failed
1503+
{
1504+
// rotate to sector angle at current position
1505+
float sn = std::sin(tpcOutCov.getAlpha());
1506+
float cs = std::cos(tpcOutCov.getAlpha());
1507+
float gy = tpcOutCov.getX() * sn + tpcOutCov.getY() * cs;
1508+
float gx = tpcOutCov.getX() * cs - tpcOutCov.getY() * sn;
1509+
if (tpcOutCov.rotate(std::atan2(gy, gx)) &&
1510+
propagator->propagateTo(tpcOutCov, kTPCTRDMiddle, false, mMaxSnp, mCoarseStep, mMatType)) {
1511+
tpcOutOK = 0;
1512+
}
1513+
}
1514+
1515+
// D2: TRD matched → kTPCTRDMiddle, rotated to SAME alpha as TPC out
1516+
trdInOK = 1; // default: not attempted
1517+
if (itTRD != tpcToTRDMap.end() && itTRD->second.hasTRDTrackPar && tpcOutOK == 0) {
1518+
trdInCov = o2::track::TrackParCov(itTRD->second.trdTrackParCov);
1519+
// rotate to TPC alpha FIRST, then propagate — guarantees same frame + same X
1520+
if (trdInCov.rotate(tpcOutCov.getAlpha()) &&
1521+
propagator->propagateTo(trdInCov, kTPCTRDMiddle, false, mMaxSnp, mCoarseStep, mMatType)) {
1522+
trdInOK = 0;
1523+
chi2PhiTPCTRD = chi2Match_Phi(tpcOutCov, trdInCov);
1524+
chi2ZTPCTRD = chi2Match_Z(tpcOutCov, trdInCov);
1525+
} else {
1526+
trdInOK = 2; // propagation failed
1527+
}
15141528
}
15151529
}
15161530

@@ -1609,6 +1623,29 @@ class TPCTimeSeries : public Task
16091623
}
16101624
double vertexTime = vertex.getTimeStamp().getTimeStamp();
16111625
double trackTime0 = trackFull.getTime0();
1626+
// Optional payload (--store-full-trackpar, default off), persisted without covariance as TrackPar.
1627+
// This statement and the main chain below fill ONE entry: TreeStream keeps its element index until "\n",
1628+
// and the flag is constant for the job, so every entry has the same branch set (a varying set silently
1629+
// drops entries). Keep both statements adjacent, with no return/continue in between, and keep the objects in
1630+
// scope until the "\n" below (TreeStream stores pointers and reads them when the entry is filled).
1631+
// tpcAtVtx = TPC track after the DCA step; its reference depends on the path:
1632+
// all tracks: DCA to (0,0,0);
1633+
// then, for ITS-TPC vertex contributors passing the ITS-TPC DCA cuts: DCA to the primary vertex;
1634+
// then, if also passing the combined DCA cut and having an ITS track: rotated to the ITS track frame and
1635+
// propagated to the X of the ITS-TPC track.
1636+
// With the switch on, these three branches come first in the tree (compare by branch name, not position).
1637+
o2::track::TrackPar tpcOutPar;
1638+
o2::track::TrackPar trdInPar;
1639+
o2::track::TrackPar tpcAtVtxPar;
1640+
if (mStoreFullTrackPar) {
1641+
tpcOutPar = tpcOutCov;
1642+
trdInPar = trdInCov;
1643+
tpcAtVtxPar = track;
1644+
*mStreamer[iThread] << "treeTimeSeries"
1645+
<< "tpcOut=" << tpcOutPar
1646+
<< "trdIn=" << trdInPar
1647+
<< "tpcAtVtx=" << tpcAtVtxPar;
1648+
}
16121649
*mStreamer[iThread] << "treeTimeSeries"
16131650
// DCAs
16141651
<< "triggerMask=" << triggerMask
@@ -1656,15 +1693,14 @@ class TPCTimeSeries : public Task
16561693
<< "nTRDTracklets=" << nTRDTracklets
16571694
<< "trdTracklets=" << trdTrackletVec
16581695
<< "trdCalibTracklets=" << trdCalibVec
1659-
// Phase 0.4: TPC out / TRD in at R=275 cm
1660-
<< "tpcOut=" << tpcOutCov
1696+
// TPC out / TRD in on the common local X = 275 cm plane: status and matching chi2
1697+
// (255 / -1 when --store-full-trackpar is off; the TrackPar objects are written above)
16611698
<< "tpcOutOK=" << tpcOutOK
1662-
<< "trdIn=" << trdInCov
16631699
<< "trdInOK=" << trdInOK
16641700
<< "chi2PhiTPCTRD=" << chi2PhiTPCTRD
16651701
<< "chi2ZTPCTRD=" << chi2ZTPCTRD
1666-
// Track parameters at vertex (full TrackParCov)
1667-
<< "tpcAtVtx=" << track
1702+
// TRD in detector readout for this run (GRPECS): 1 yes, 0 no, 255 unknown
1703+
<< "trdInReadout=" << mTRDInReadout
16681704
<< "chi2match_ITSTPC=" << chi2match_ITSTPC
16691705
<< "PID=" << trkOrig.getPID().getID()
16701706
// TPC cov at vertex (without vertex constrained)
@@ -2056,7 +2092,7 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter,
20562092

20572093
const bool enableAskMatLUT = matType == o2::base::Propagator::MatCorrType::USEMatCorrLUT;
20582094
auto ccdbRequest = std::make_shared<o2::base::GRPGeomRequest>(!disableWriter, // orbitResetTime
2059-
false, // GRPECS=true for nHBF per TF
2095+
enableUnbinnedWriter, // GRPECS only when the unbinned trdInReadout can be written
20602096
false, // GRPLHCIF
20612097
true, // GRPMagField
20622098
enableAskMatLUT, // askMatLUT
@@ -2113,6 +2149,7 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter,
21132149
{"sample-unbinned-tsallis", VariantType::Bool, false, {"Perform sampling of unbinned data based on Tsallis function"}},
21142150
{"sampling-factor", VariantType::Float, 0.001f, {"Sampling factor in case sample-unbinned-tsallis is used"}},
21152151
{"disable-min-bias-trigger", VariantType::Bool, false, {"Disable the minimum bias trigger for skimmed data"}},
2152+
{"store-full-trackpar", VariantType::Bool, false, {"Experimental: compute TPC-out/TRD-in states on the common local X = 275 cm plane and the TPC-TRD matching chi2, and store tpcOut, trdIn, tpcAtVtx as TrackPar in the unbinned tree"}},
21162153
{"out-file-unbinned", VariantType::String, "time_series_tracks.root", {"name of the output file for the unbinned data"}},
21172154
{"max-occupancy-bins", VariantType::Int, 912, {"Maximum number of occupancy bins"}}}};
21182155
}

0 commit comments

Comments
 (0)