Skip to content

Commit 2ce8a00

Browse files
TPC SCD: store the MC truth of the residual tracks (--enable-mc)
The interpolation workflow had MC switched off ("not yet implemented"). With --enable-mc (opt-in, since the workflow also runs on data without --disable-mc) and --send-track-data, a TrackDataMC vector aligned 1:1 with the TrackData output is sent as GLO/TRKDATAMC: MC labels of the ITS-TPC part of the seeding track and of its ITS and TPC parts (flag for a fake ITS-TPC match), the truth at the ITS outer parameters (nearest ITS track reference, propagated with the workflow's material correction and the true mass to the x and alpha of TrackData::par, with the distance to that reference), the truth at the TPC entrance (first TPC track reference in time, sector frame) with the distance between it, propagated to the innermost TPC cluster of the track, and that cluster (flags loopers, wrong legs and fakes), and the PDG code. Only the ITS, TPC and ITS-TPC track labels are read; the kinematics are looked up event by event and released after each event. The residual aggregator writes the truth as branch trkMC of the trackData tree with --enable-mc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 8ae4f15 commit 2ce8a00

9 files changed

Lines changed: 271 additions & 23 deletions

File tree

‎Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
#include "DetectorsBase/GRPGeomHelper.h"
2424
#include "TPCCalibration/VDriftHelper.h"
2525
#include "DataFormatsITSMFT/TopologyDictionary.h"
26+
#include "Steer/MCKinematicsReader.h"
2627

2728
using namespace o2::framework;
2829

2930
namespace o2::globaltracking
3031
{
3132
struct DataRequest;
33+
struct RecoContainer;
3234
} // namespace o2::globaltracking
3335

3436
namespace o2
@@ -40,14 +42,15 @@ class TPCInterpolationDPL : public Task
4042
public:
4143
TPCInterpolationDPL(std::shared_ptr<o2::globaltracking::DataRequest> dr, o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap, std::shared_ptr<o2::base::GRPGeomRequest> gr, bool useMC,
4244
bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid) : mDataRequest(dr), mSources(src), mSourcesMap(srcMap), mGGCCDBRequest(gr), mUseMC(useMC), mProcessITSTPConly(processITSTPConly), mSendTrackData(sendTrackData), mDebugOutput(debugOutput), mExtDetResid(extDetResid) {}
43-
~TPCInterpolationDPL() override = default;
45+
~TPCInterpolationDPL() override;
4446
void init(InitContext& ic) final;
4547
void run(ProcessingContext& pc) final;
4648
void endOfStream(EndOfStreamContext& ec) final;
4749
void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final;
4850

4951
private:
5052
void updateTimeDependentParams(ProcessingContext& pc);
53+
void fillMCTruth(const o2::globaltracking::RecoContainer& recoData);
5154
o2::tpc::TrackInterpolation mInterpolation; ///< track interpolation engine
5255
std::shared_ptr<o2::globaltracking::DataRequest> mDataRequest; ///< steers the input
5356
std::shared_ptr<o2::base::GRPGeomRequest> mGGCCDBRequest;
@@ -56,6 +59,8 @@ class TPCInterpolationDPL : public Task
5659
o2::dataformats::GlobalTrackID::mask_t mSources{}; ///< which input sources are configured
5760
o2::dataformats::GlobalTrackID::mask_t mSourcesMap{}; ///< possible subset of mSources specifically for map creation
5861
bool mUseMC{false}; ///< MC flag
62+
std::unique_ptr<o2::steer::MCKinematicsReader> mMCReader; ///< MC kinematics and track references (MC only)
63+
std::vector<TrackDataMC> mTrackDataMC; ///< MC truth aligned with the TrackData output (MC only)
5964
bool mProcessITSTPConly{false}; ///< should also tracks without outer point (ITS-TPC only) be processed?
6065
bool mProcessSeeds{false}; ///< process not only most complete track, but also its shorter parts
6166
bool mDebugOutput{false}; ///< add more information to the output (track points of ITS, TRD and TOF)

‎Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace calibration
4141
class ResidualAggregatorDevice : public o2::framework::Task
4242
{
4343
public:
44-
ResidualAggregatorDevice(std::shared_ptr<o2::base::GRPGeomRequest> req, bool trackInput, bool ctpInput, bool writeUnbinnedResiduals, bool writeBinnedResiduals, bool writeTrackData, std::shared_ptr<o2::globaltracking::DataRequest> dataRequest) : mCCDBRequest(req), mTrackInput(trackInput), mCTPInput(ctpInput), mWriteUnbinnedResiduals(writeUnbinnedResiduals), mWriteBinnedResiduals(writeBinnedResiduals), mWriteTrackData(writeTrackData), mDataRequest(dataRequest) {}
44+
ResidualAggregatorDevice(std::shared_ptr<o2::base::GRPGeomRequest> req, bool trackInput, bool ctpInput, bool writeUnbinnedResiduals, bool writeBinnedResiduals, bool writeTrackData, bool mcInput, std::shared_ptr<o2::globaltracking::DataRequest> dataRequest) : mCCDBRequest(req), mTrackInput(trackInput), mCTPInput(ctpInput), mWriteUnbinnedResiduals(writeUnbinnedResiduals), mWriteBinnedResiduals(writeBinnedResiduals), mWriteTrackData(writeTrackData), mMCInput(mcInput), mDataRequest(dataRequest) {}
4545

4646
void init(o2::framework::InitContext& ic) final
4747
{
@@ -97,6 +97,7 @@ class ResidualAggregatorDevice : public o2::framework::Task
9797
mAggregator->setWriteBinnedResiduals(mWriteBinnedResiduals);
9898
mAggregator->setWriteUnbinnedResiduals(mWriteUnbinnedResiduals);
9999
mAggregator->setWriteTrackData(mWriteTrackData);
100+
mAggregator->setWriteTrackDataMC(mMCInput);
100101
mAggregator->setCompression(ic.options().get<int>("compression"));
101102
}
102103

@@ -141,6 +142,14 @@ class ResidualAggregatorDevice : public o2::framework::Task
141142
trkData.emplace(pc.inputs().get<gsl::span<o2::tpc::TrackData>>("trkData"));
142143
trkDataPtr = &trkData.value();
143144
}
145+
// MC truth of the track data (optional, MC only)
146+
const gsl::span<const o2::tpc::TrackDataMC>* trkDataMCPtr = nullptr;
147+
using trkDataMCType = std::decay_t<decltype(pc.inputs().get<gsl::span<o2::tpc::TrackDataMC>>(""))>;
148+
std::optional<trkDataMCType> trkDataMC;
149+
if (mMCInput) {
150+
trkDataMC.emplace(pc.inputs().get<gsl::span<o2::tpc::TrackDataMC>>("trkDataMC"));
151+
trkDataMCPtr = &trkDataMC.value();
152+
}
144153
// CTP lumi input (optional)
145154
const o2::ctp::LumiInfo* lumi = nullptr;
146155
using lumiDataType = std::decay_t<decltype(pc.inputs().get<o2::ctp::LumiInfo>(""))>;
@@ -152,7 +161,7 @@ class ResidualAggregatorDevice : public o2::framework::Task
152161

153162
o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mAggregator->getCurrentTFInfo());
154163
LOG(detail) << "Processing TF " << mAggregator->getCurrentTFInfo().tfCounter << " with " << trkData->size() << " tracks and " << residualsData.size() << " unbinned residuals associated to them";
155-
mAggregator->process(residualsData, residualsDataDet, trackRefs, trkDataPtr, lumi);
164+
mAggregator->process(residualsData, residualsDataDet, trackRefs, trkDataPtr, trkDataMCPtr, lumi);
156165
std::chrono::duration<double, std::milli> runDuration = std::chrono::high_resolution_clock::now() - runStartTime;
157166
LOGP(debug, "Duration for run method: {} ms. From this taken for time dependent param update: {} ms",
158167
std::chrono::duration_cast<std::chrono::milliseconds>(runDuration).count(),
@@ -205,6 +214,7 @@ class ResidualAggregatorDevice : public o2::framework::Task
205214
bool mWriteBinnedResiduals{false}; ///< flag, whether to write binned residuals to output file
206215
bool mWriteUnbinnedResiduals{false}; ///< flag, whether to write unbinned residuals to output file
207216
bool mWriteTrackData{false}; ///< flag, whether to write track data to output file
217+
bool mMCInput{false}; ///< flag whether to expect the MC truth of the track data as input
208218
bool mRunStopRequested{false}; ///< flag in case the run was stopped
209219
bool mInitDone{false}; ///< flag whether initialization was done for current run
210220
};
@@ -214,7 +224,7 @@ class ResidualAggregatorDevice : public o2::framework::Task
214224
namespace framework
215225
{
216226

217-
DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, bool writeUnbinnedResiduals, bool writeBinnedResiduals, bool writeTrackData)
227+
DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, bool writeUnbinnedResiduals, bool writeBinnedResiduals, bool writeTrackData, bool mcInput = false)
218228
{
219229
std::shared_ptr<o2::globaltracking::DataRequest> dataRequest = std::make_shared<o2::globaltracking::DataRequest>();
220230
if (ctpInput) {
@@ -227,6 +237,9 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b
227237
inputs.emplace_back("trackRefs", "GLO", "TRKREFS");
228238
if (trackInput) {
229239
inputs.emplace_back("trkData", "GLO", "TRKDATA");
240+
if (mcInput) {
241+
inputs.emplace_back("trkDataMC", "GLO", "TRKDATAMC");
242+
}
230243
}
231244
auto ccdbRequest = std::make_shared<o2::base::GRPGeomRequest>(true, // orbitResetTime
232245
true, // GRPECS=true
@@ -240,7 +253,7 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b
240253
"residual-aggregator",
241254
inputs,
242255
Outputs{},
243-
AlgorithmSpec{adaptFromTask<o2::calibration::ResidualAggregatorDevice>(ccdbRequest, trackInput, ctpInput, writeUnbinnedResiduals, writeBinnedResiduals, writeTrackData, dataRequest)},
256+
AlgorithmSpec{adaptFromTask<o2::calibration::ResidualAggregatorDevice>(ccdbRequest, trackInput, ctpInput, writeUnbinnedResiduals, writeBinnedResiduals, writeTrackData, trackInput && mcInput, dataRequest)},
244257
Options{
245258
{"sec-per-slot", VariantType::UInt32, 600u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}},
246259
{"updateInterval", VariantType::UInt32, 6'000u, {"update interval in number of TFs (only used in case slot length is infinite)"}},

‎Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx‎

Lines changed: 186 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
#include <vector>
1515
#include <unordered_map>
16+
#include <algorithm>
17+
#include <cmath>
1618

1719
#include "DataFormatsITS/TrackITS.h"
1820
#include "ITSBase/GeometryTGeo.h"
@@ -33,6 +35,9 @@
3335
#include "Framework/ConfigParamRegistry.h"
3436
#include "Framework/ControlService.h"
3537
#include "Framework/DeviceSpec.h"
38+
#include "Steer/MCKinematicsReader.h"
39+
#include "SimulationDataFormat/TrackReference.h"
40+
#include "SimulationDataFormat/O2DatabasePDG.h"
3641

3742
using namespace o2::framework;
3843
using namespace o2::globaltracking;
@@ -44,6 +49,8 @@ namespace o2
4449
namespace tpc
4550
{
4651

52+
TPCInterpolationDPL::~TPCInterpolationDPL() = default;
53+
4754
void TPCInterpolationDPL::init(InitContext& ic)
4855
{
4956
//-------- init geometry and field --------//
@@ -59,6 +66,16 @@ void TPCInterpolationDPL::init(InitContext& ic)
5966
int lane = ic.services().get<const o2::framework::DeviceSpec>().inputTimesliceId;
6067
int maxLanes = ic.services().get<const o2::framework::DeviceSpec>().maxInputTimeslices;
6168
mInterpolation.setLane(lane, maxLanes);
69+
if (mUseMC) {
70+
if (!mSendTrackData) {
71+
LOG(warning) << "MC truth is stored aligned with the track data, but send-track-data is not set: no MC truth will be sent";
72+
}
73+
mMCReader = std::make_unique<o2::steer::MCKinematicsReader>();
74+
auto mcContext = ic.options().get<std::string>("mc-collision-context");
75+
if (!mMCReader->initFromDigitContext(mcContext)) {
76+
LOG(fatal) << "Could not initialize the MC kinematics reader from " << mcContext;
77+
}
78+
}
6279
}
6380

6481
void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc)
@@ -149,9 +166,168 @@ void TPCInterpolationDPL::run(ProcessingContext& pc)
149166
if (mDebugOutput) {
150167
pc.outputs().snapshot(Output{"GLO", "TRKDATAEXT", 0}, mInterpolation.getTrackDataExtended());
151168
}
169+
if (mUseMC && mSendTrackData) {
170+
fillMCTruth(recoData);
171+
pc.outputs().snapshot(Output{"GLO", "TRKDATAMC", 0}, mTrackDataMC);
172+
}
152173
mInterpolation.reset();
153174
}
154175

176+
void TPCInterpolationDPL::fillMCTruth(const RecoContainer& recoData)
177+
{
178+
// MC truth for every stored TrackData: labels of the ITS-TPC part of the seed and of its ITS and TPC parts, the truth at
179+
// the ITS outer parameters (the ITS track reference nearest to TrackData::par, propagated to its x with the material
180+
// correction of the workflow and the mass of the true particle) and the truth at the TPC entrance (the first TPC track
181+
// reference in time, in the sector frame)
182+
const auto& trkData = mInterpolation.getReferenceTracks();
183+
mTrackDataMC.clear();
184+
mTrackDataMC.resize(trkData.size());
185+
struct Lookup {
186+
o2::MCCompLabel lbl;
187+
uint32_t idx;
188+
bool its; // ITS outer (true) or TPC entrance (false)
189+
};
190+
std::vector<Lookup> lookups;
191+
lookups.reserve(2 * trkData.size());
192+
for (size_t i = 0; i < trkData.size(); ++i) {
193+
auto& mc = mTrackDataMC[i];
194+
auto gidSet = recoData.getSingleDetectorRefs(trkData[i].gid);
195+
auto gidITS = gidSet[GTrackID::ITS].isIndexSet() ? gidSet[GTrackID::ITS] : gidSet[GTrackID::ITSAB];
196+
if (gidSet[GTrackID::ITSTPC].isIndexSet()) {
197+
mc.label = recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPC]);
198+
}
199+
if (gidITS.isIndexSet()) {
200+
mc.labelITS = recoData.getTrackMCLabel(gidITS);
201+
}
202+
if (gidSet[GTrackID::TPC].isIndexSet()) {
203+
mc.labelTPC = recoData.getTrackMCLabel(gidSet[GTrackID::TPC]);
204+
}
205+
if (mc.labelITS.isValid() && mc.labelTPC.isValid() && mc.labelITS.getTrackEventSourceID() != mc.labelTPC.getTrackEventSourceID()) {
206+
mc.flags |= TrackDataMC::FakeITSTPC;
207+
}
208+
const auto& lblITS = mc.labelITS.isValid() ? mc.labelITS : mc.label;
209+
const auto& lblTPC = mc.labelTPC.isValid() ? mc.labelTPC : mc.label;
210+
if (lblITS.isValid()) {
211+
lookups.push_back({lblITS, uint32_t(i), true});
212+
}
213+
if (lblTPC.isValid()) {
214+
lookups.push_back({lblTPC, uint32_t(i), false});
215+
}
216+
}
217+
// the reader loads the kinematics of a whole event (can be >100 MB): process event by event and release it right after
218+
std::sort(lookups.begin(), lookups.end(), [](const Lookup& a, const Lookup& b) {
219+
return a.lbl.getSourceID() != b.lbl.getSourceID() ? a.lbl.getSourceID() < b.lbl.getSourceID() : a.lbl.getEventID() < b.lbl.getEventID();
220+
});
221+
auto pdgToPID = [](int pdg) {
222+
switch (std::abs(pdg)) {
223+
case 11:
224+
return o2::track::PID(o2::track::PID::Electron);
225+
case 13:
226+
return o2::track::PID(o2::track::PID::Muon);
227+
case 321:
228+
return o2::track::PID(o2::track::PID::Kaon);
229+
case 2212:
230+
return o2::track::PID(o2::track::PID::Proton);
231+
case 1000010020:
232+
return o2::track::PID(o2::track::PID::Deuteron);
233+
case 1000010030:
234+
return o2::track::PID(o2::track::PID::Triton);
235+
case 1000020030:
236+
return o2::track::PID(o2::track::PID::Helium3);
237+
case 1000020040:
238+
return o2::track::PID(o2::track::PID::Alpha);
239+
default:
240+
return o2::track::PID(o2::track::PID::Pion);
241+
}
242+
};
243+
auto refToPar = [&pdgToPID](const o2::TrackReference& ref, int charge, int pdg, bool sectorAlpha) {
244+
std::array<float, 3> xyz{ref.X(), ref.Y(), ref.Z()};
245+
std::array<float, 3> pxyz{ref.Px(), ref.Py(), ref.Pz()};
246+
return o2::track::TrackPar(xyz, pxyz, charge, sectorAlpha, pdgToPID(pdg));
247+
};
248+
const auto matCorr = static_cast<o2::base::Propagator::MatCorrType>(mMatCorr);
249+
auto prop = o2::base::Propagator::Instance();
250+
int curSrc = -1;
251+
int curEv = -1;
252+
for (const auto& lk : lookups) {
253+
const auto& lbl = lk.lbl;
254+
if (lbl.getSourceID() != curSrc || lbl.getEventID() != curEv) {
255+
if (curSrc >= 0) {
256+
mMCReader->releaseTracksForSourceAndEvent(curSrc, curEv);
257+
}
258+
curSrc = lbl.getSourceID();
259+
curEv = lbl.getEventID();
260+
}
261+
const auto& trk = trkData[lk.idx];
262+
auto& mc = mTrackDataMC[lk.idx];
263+
const auto* mcTrk = mMCReader->getTrack(lbl);
264+
int pdg = mcTrk ? mcTrk->GetPdgCode() : 0;
265+
const auto* pPDG = mcTrk ? O2DatabasePDG::Instance()->GetParticle(pdg) : nullptr;
266+
int charge = pPDG ? int(std::lround(pPDG->Charge() / 3.)) : 0; // TParticlePDG charge is in units of |e|/3
267+
auto refs = mMCReader->getTrackRefs(lbl.getSourceID(), lbl.getEventID(), lbl.getTrackID());
268+
if (lk.its) { // ITS outer: track reference of the ITS part nearest to TrackData::par
269+
mc.pdg = pdg;
270+
const o2::TrackReference* best = nullptr;
271+
float bestD2 = 1e30f;
272+
auto xyzReco = trk.par.getXYZGlo();
273+
for (const auto& ref : refs) {
274+
if (ref.getDetectorId() != DetID::ITS) {
275+
continue;
276+
}
277+
float dx = ref.X() - xyzReco.X();
278+
float dy = ref.Y() - xyzReco.Y();
279+
float dz = ref.Z() - xyzReco.Z();
280+
float d2 = dx * dx + dy * dy + dz * dz;
281+
if (d2 < bestD2) {
282+
bestD2 = d2;
283+
best = &ref;
284+
}
285+
}
286+
if (best && charge) {
287+
auto par = refToPar(*best, charge, pdg, false);
288+
if (par.rotateParam(trk.par.getAlpha()) && prop->PropagateToXBxByBz(par, trk.par.getX(), 0.999f, o2::base::Propagator::MAX_STEP, matCorr)) {
289+
mc.parITSOut = par;
290+
mc.distITSRef = std::sqrt(bestD2);
291+
mc.flags |= TrackDataMC::HasITSOut;
292+
}
293+
}
294+
} else { // TPC entrance: first TPC track reference in time of the TPC part
295+
if (!mc.labelITS.isValid() && !mc.label.isValid()) {
296+
mc.pdg = pdg; // no ITS lookup for this track
297+
}
298+
const o2::TrackReference* first = nullptr;
299+
for (const auto& ref : refs) {
300+
if (ref.getDetectorId() == DetID::TPC && (!first || ref.getTime() < first->getTime())) {
301+
first = &ref;
302+
}
303+
}
304+
if (first && charge) {
305+
mc.parTPCIn = refToPar(*first, charge, pdg, true);
306+
mc.flags |= TrackDataMC::HasTPCIn;
307+
// association check (loopers, wrong leg, fake): truth at the innermost TPC cluster of the track vs that cluster
308+
const auto& clRes = mInterpolation.getClusterResiduals();
309+
const UnbinnedResid* inner = nullptr;
310+
for (int ic = trk.clIdx.getFirstEntry(); ic < trk.clIdx.getFirstEntry() + trk.clIdx.getEntries(); ++ic) {
311+
if (clRes[ic].row < constants::MAXGLOBALPADROW && (!inner || clRes[ic].row < inner->row)) {
312+
inner = &clRes[ic];
313+
}
314+
}
315+
if (inner) {
316+
auto par = mc.parTPCIn;
317+
float yCl = inner->y * param::MaxY / 0x7fff + inner->dy * param::MaxResid / 0x7fff;
318+
float zCl = inner->z * param::MaxZ / 0x7fff + inner->dz * param::MaxResid / 0x7fff;
319+
if (par.rotateParam(o2::math_utils::sector2Angle(inner->sec)) && prop->PropagateToXBxByBz(par, param::RowX[inner->row], 0.999f, o2::base::Propagator::MAX_STEP, matCorr)) {
320+
mc.distTPCRef = std::hypot(par.getY() - yCl, par.getZ() - zCl);
321+
}
322+
}
323+
}
324+
}
325+
}
326+
if (curSrc >= 0) {
327+
mMCReader->releaseTracksForSourceAndEvent(curSrc, curEv);
328+
}
329+
}
330+
155331
void TPCInterpolationDPL::endOfStream(EndOfStreamContext& ec)
156332
{
157333
mInterpolation.finalize();
@@ -165,14 +341,13 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas
165341
dataRequest->setITSPerLayer(itsStag);
166342
std::vector<OutputSpec> outputs;
167343

168-
if (useMC) {
169-
LOG(fatal) << "MC usage must be disabled for this workflow, since it is not yet implemented";
344+
dataRequest->requestTracks(srcVtx, false);
345+
dataRequest->requestClusters(srcCls, false);
346+
dataRequest->requestPrimaryVertices(false);
347+
if (useMC) { // the MC truth needs only the labels of the ITS-TPC tracks and of their ITS and TPC parts
348+
dataRequest->requestTracks(GTrackID::getSourcesMask("ITS,TPC,ITS-TPC"), true);
170349
}
171350

172-
dataRequest->requestTracks(srcVtx, useMC);
173-
dataRequest->requestClusters(srcCls, useMC);
174-
dataRequest->requestPrimaryVertices(useMC);
175-
176351
auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime
177352
true, // GRPECS=true
178353
true, // GRPLHCIF
@@ -191,6 +366,9 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas
191366
if (debugOutput) {
192367
outputs.emplace_back("GLO", "TRKDATAEXT", 0, Lifetime::Timeframe);
193368
}
369+
if (useMC && sendTrackData) {
370+
outputs.emplace_back("GLO", "TRKDATAMC", 0, Lifetime::Timeframe);
371+
}
194372

195373
return DataProcessorSpec{
196374
"tpc-track-interpolation",
@@ -200,7 +378,8 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas
200378
Options{
201379
{"matCorrType", VariantType::Int, 2, {"material correction type (definition in Propagator.h)"}},
202380
{"sec-per-slot", VariantType::UInt32, 300u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}},
203-
{"process-seeds", VariantType::Bool, false, {"do not remove duplicates, e.g. for ITS-TPC-TRD track also process its seeding ITS-TPC part"}}}};
381+
{"process-seeds", VariantType::Bool, false, {"do not remove duplicates, e.g. for ITS-TPC-TRD track also process its seeding ITS-TPC part"}},
382+
{"mc-collision-context", VariantType::String, "collisioncontext.root", {"collision context used to access the MC kinematics and track references (MC only)"}}}};
204383
}
205384

206385
} // namespace tpc

0 commit comments

Comments
 (0)