Project
Loading...
Searching...
No Matches
AODProducerWorkflowSpec.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
41#include "MathUtils/Utils.h"
51#include "Framework/DataTypes.h"
55#include "FT0Base/Geometry.h"
80#include "ZDCBase/Constants.h"
83#include "TOFBase/Utils.h"
84#include "O2Version.h"
85#include "TMath.h"
86#include "MathUtils/Utils.h"
87#include "Math/SMatrix.h"
88#include "TString.h"
89#include <limits>
90#include <map>
91#include <numeric>
92#include <type_traits>
93#include <unordered_map>
94#include <set>
95#include <string>
96#include <vector>
97#include <thread>
98#include "TLorentzVector.h"
99#include "TVector3.h"
100#include "MathUtils/Tsallis.h"
101#include <random>
102#ifdef WITH_OPENMP
103#include <omp.h>
104#endif
105
106using namespace o2::framework;
107using namespace o2::math_utils::detail;
114
115namespace o2::aodproducer
116{
117
118void AODProducerWorkflowDPL::createCTPReadout(const o2::globaltracking::RecoContainer& recoData, std::vector<o2::ctp::CTPDigit>& ctpDigits, ProcessingContext& pc)
119{
120 // Extraxt CTP Config from CCDB
121 const auto ctpcfg = pc.inputs().get<o2::ctp::CTPConfiguration*>("ctpconfig");
122 ctpcfg->printStream(std::cout);
123 // o2::ctp::CTPConfiguration ctpcfg = o2::ctp::CTPRunManager::getConfigFromCCDB(-1, std::to_string(runNumber)); // how to get run
124 // Extract inputs from recoData
125 uint64_t classMaskEMCAL = 0, classMaskTRD = 0, classMaskPHOSCPV = 0;
126 for (const auto& trgclass : ctpcfg->getCTPClasses()) {
127 if (trgclass.cluster->getClusterDetNames().find("EMC") != std::string::npos) {
128 classMaskEMCAL = trgclass.classMask;
129 }
130 if (trgclass.cluster->getClusterDetNames().find("PHS") != std::string::npos) {
131 classMaskPHOSCPV = trgclass.classMask;
132 }
133 if (trgclass.cluster->getClusterDetNames().find("TRD") != std::string::npos) {
134 classMaskTRD = trgclass.classMask;
135 }
136 }
137 LOG(info) << "createCTPReadout: Class Mask EMCAL -> " << classMaskEMCAL;
138 LOG(info) << "createCTPReadout: Class Mask PHOS/CPV -> " << classMaskPHOSCPV;
139 LOG(info) << "createCTPReadout: Class Mask TRD -> " << classMaskTRD;
140
141 // const auto& fddRecPoints = recoData.getFDDRecPoints();
142 // const auto& fv0RecPoints = recoData.getFV0RecPoints();
143 const auto& triggerrecordEMCAL = recoData.getEMCALTriggers();
144 const auto& triggerrecordPHOSCPV = recoData.getPHOSTriggers();
145 const auto& triggerrecordTRD = recoData.getTRDTriggerRecords();
146 // For EMCAL filter remove calibration triggers
147 std::vector<o2::emcal::TriggerRecord> triggerRecordEMCALPhys;
148 for (const auto& trg : triggerrecordEMCAL) {
149 if (trg.getTriggerBits() & o2::trigger::Cal) {
150 continue;
151 }
152 triggerRecordEMCALPhys.push_back(trg);
153 }
154 // const auto& triggerrecordTRD =recoData.getITSTPCTRDTriggers()
155 //
156
157 // Find TVX triggers, only TRD/EMCAL/PHOS/CPV triggers in coincidence will be accepted
158 std::set<uint64_t> bcsMapT0triggers;
159 const auto& ft0RecPoints = recoData.getFT0RecPoints();
160 for (auto& ft0RecPoint : ft0RecPoints) {
161 auto t0triggers = ft0RecPoint.getTrigger();
162 if (t0triggers.getVertex()) {
163 uint64_t globalBC = ft0RecPoint.getInteractionRecord().toLong();
164 bcsMapT0triggers.insert(globalBC);
165 }
166 }
167
168 auto genericCTPDigitizer = [&bcsMapT0triggers, &ctpDigits](auto triggerrecords, uint64_t classmask) -> int {
169 // Strategy:
170 // find detector trigger based on trigger record from readout and add CTPDigit if trigger there
171 int cntwarnings = 0;
172 uint32_t orbitPrev = 0;
173 uint16_t bcPrev = 0;
174 for (auto& trigger : triggerrecords) {
175 auto orbitPrevT = orbitPrev;
176 auto bcPrevT = bcPrev;
177 bcPrev = trigger.getBCData().bc;
178 orbitPrev = trigger.getBCData().orbit;
179 // dedicated for TRD: remove bogus triggers
180 if (orbitPrev < orbitPrevT || bcPrev >= o2::constants::lhc::LHCMaxBunches || (orbitPrev == orbitPrevT && bcPrev < bcPrevT)) {
181 cntwarnings++;
182 // LOGP(warning, "Bogus TRD trigger at bc:{}/orbit:{} (previous was {}/{}), with {} tracklets and {} digits",bcPrev, orbitPrev, bcPrevT, orbitPrevT, trig.getNumberOfTracklets(), trig.getNumberOfDigits());
183 } else {
184 uint64_t globalBC = trigger.getBCData().toLong();
185 auto t0entry = bcsMapT0triggers.find(globalBC);
186 if (t0entry != bcsMapT0triggers.end()) {
187 auto ctpdig = std::find_if(ctpDigits.begin(), ctpDigits.end(), [globalBC](const o2::ctp::CTPDigit& dig) { return static_cast<uint64_t>(dig.intRecord.toLong()) == globalBC; });
188 if (ctpdig != ctpDigits.end()) {
189 // CTP digit existing from other trigger, merge detector class mask
190 ctpdig->CTPClassMask |= std::bitset<64>(classmask);
191 LOG(debug) << "createCTPReadout: Merging " << classmask << " CTP digits with existing digit, CTP mask " << ctpdig->CTPClassMask;
192 } else {
193 // New CTP digit needed
194 LOG(debug) << "createCTPReadout: New CTP digit needed for class " << classmask << std::endl;
195 auto& ctpdigNew = ctpDigits.emplace_back();
196 ctpdigNew.intRecord.setFromLong(globalBC);
197 ctpdigNew.CTPClassMask = classmask;
198 }
199 } else {
200 LOG(warning) << "createCTPReadout: Found " << classmask << " and no MTVX:" << globalBC;
201 }
202 }
203 }
204 return cntwarnings;
205 };
206
207 auto warningsTRD = genericCTPDigitizer(triggerrecordTRD, classMaskTRD);
208 auto warningsEMCAL = genericCTPDigitizer(triggerRecordEMCALPhys, classMaskEMCAL);
209 auto warningsPHOSCPV = genericCTPDigitizer(triggerrecordPHOSCPV, classMaskPHOSCPV);
210
211 LOG(info) << "createCTPReadout:# of TRD bogus triggers:" << warningsTRD;
212 LOG(info) << "createCTPReadout:# of EMCAL bogus triggers:" << warningsEMCAL;
213 LOG(info) << "createCTPReadout:# of PHOS/CPV bogus triggers:" << warningsPHOSCPV;
214}
215
216void AODProducerWorkflowDPL::collectBCs(const o2::globaltracking::RecoContainer& data,
217 const std::vector<o2::InteractionTimeRecord>& mcRecords,
218 std::map<uint64_t, int>& bcsMap)
219{
220 const auto& primVertices = data.getPrimaryVertices();
221 const auto& fddRecPoints = data.getFDDRecPoints();
222 const auto& ft0RecPoints = data.getFT0RecPoints();
223 const auto& fv0RecPoints = data.getFV0RecPoints();
224 const auto& caloEMCCellsTRGR = data.getEMCALTriggers();
225 const auto& caloPHOSCellsTRGR = data.getPHOSTriggers();
226 const auto& cpvTRGR = data.getCPVTriggers();
227 const auto& ctpDigits = data.getCTPDigits();
228 const auto& zdcBCRecData = data.getZDCBCRecData();
229
230 bcsMap[mStartIR.toLong()] = 1; // store the start of TF
231
232 // collecting non-empty BCs and enumerating them
233 for (auto& rec : mcRecords) {
234 uint64_t globalBC = rec.toLong();
235 bcsMap[globalBC] = 1;
236 }
237
238 for (auto& fddRecPoint : fddRecPoints) {
239 uint64_t globalBC = fddRecPoint.getInteractionRecord().toLong();
240 bcsMap[globalBC] = 1;
241 }
242
243 for (auto& ft0RecPoint : ft0RecPoints) {
244 uint64_t globalBC = ft0RecPoint.getInteractionRecord().toLong();
245 bcsMap[globalBC] = 1;
246 }
247
248 for (auto& fv0RecPoint : fv0RecPoints) {
249 uint64_t globalBC = fv0RecPoint.getInteractionRecord().toLong();
250 bcsMap[globalBC] = 1;
251 }
252
253 for (auto& zdcRecData : zdcBCRecData) {
254 uint64_t globalBC = zdcRecData.ir.toLong();
255 bcsMap[globalBC] = 1;
256 }
257
258 for (auto& vertex : primVertices) {
259 auto& timeStamp = vertex.getTimeStamp();
260 double tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns
261 uint64_t globalBC = relativeTime_to_GlobalBC(tsTimeStamp);
262 bcsMap[globalBC] = 1;
263 }
264
265 for (auto& emcaltrg : caloEMCCellsTRGR) {
266 uint64_t globalBC = emcaltrg.getBCData().toLong();
267 bcsMap[globalBC] = 1;
268 }
269
270 for (auto& phostrg : caloPHOSCellsTRGR) {
271 uint64_t globalBC = phostrg.getBCData().toLong();
272 bcsMap[globalBC] = 1;
273 }
274
275 for (auto& cpvtrg : cpvTRGR) {
276 uint64_t globalBC = cpvtrg.getBCData().toLong();
277 bcsMap[globalBC] = 1;
278 }
279
280 for (auto& ctpDigit : ctpDigits) {
281 uint64_t globalBC = ctpDigit.intRecord.toLong();
282 bcsMap[globalBC] = 1;
283 }
284
285 int bcID = 0;
286 for (auto& item : bcsMap) {
287 item.second = bcID;
288 bcID++;
289 }
290}
291
292template <typename TracksCursorType, typename TracksCovCursorType>
293void AODProducerWorkflowDPL::addToTracksTable(TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor,
294 const o2::track::TrackParCov& track, int collisionID, aod::track::TrackTypeEnum type)
295{
296 tracksCursor(collisionID,
297 type,
298 truncateFloatFraction(track.getX(), mTrackX),
299 truncateFloatFraction(track.getAlpha(), mTrackAlpha),
300 track.getY(),
301 track.getZ(),
302 truncateFloatFraction(track.getSnp(), mTrackSnp),
303 truncateFloatFraction(track.getTgl(), mTrackTgl),
304 truncateFloatFraction(track.getQ2Pt(), mTrack1Pt));
305 // trackscov
306 float sY = TMath::Sqrt(track.getSigmaY2()), sZ = TMath::Sqrt(track.getSigmaZ2()), sSnp = TMath::Sqrt(track.getSigmaSnp2()),
307 sTgl = TMath::Sqrt(track.getSigmaTgl2()), sQ2Pt = TMath::Sqrt(track.getSigma1Pt2());
308 tracksCovCursor(truncateFloatFraction(sY, mTrackCovDiag),
309 truncateFloatFraction(sZ, mTrackCovDiag),
310 truncateFloatFraction(sSnp, mTrackCovDiag),
311 truncateFloatFraction(sTgl, mTrackCovDiag),
312 truncateFloatFraction(sQ2Pt, mTrackCovDiag),
313 (Char_t)(128. * track.getSigmaZY() / (sZ * sY)),
314 (Char_t)(128. * track.getSigmaSnpY() / (sSnp * sY)),
315 (Char_t)(128. * track.getSigmaSnpZ() / (sSnp * sZ)),
316 (Char_t)(128. * track.getSigmaTglY() / (sTgl * sY)),
317 (Char_t)(128. * track.getSigmaTglZ() / (sTgl * sZ)),
318 (Char_t)(128. * track.getSigmaTglSnp() / (sTgl * sSnp)),
319 (Char_t)(128. * track.getSigma1PtY() / (sQ2Pt * sY)),
320 (Char_t)(128. * track.getSigma1PtZ() / (sQ2Pt * sZ)),
321 (Char_t)(128. * track.getSigma1PtSnp() / (sQ2Pt * sSnp)),
322 (Char_t)(128. * track.getSigma1PtTgl() / (sQ2Pt * sTgl)));
323}
324
325template <typename TracksExtraCursorType>
326void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracksExtraCursor, TrackExtraInfo& extraInfoHolder)
327{
328 // In case of TPC-only tracks, do not truncate the time error since we encapsulate there a special encoding of
329 // the deltaFwd/Bwd times
330 auto trackTimeRes = extraInfoHolder.trackTimeRes;
331 if (!extraInfoHolder.isTPConly) {
332 trackTimeRes = truncateFloatFraction(trackTimeRes, mTrackTimeError);
333 }
334
335 // extra
336 tracksExtraCursor(truncateFloatFraction(extraInfoHolder.tpcInnerParam, mTrack1Pt),
337 extraInfoHolder.flags,
338 extraInfoHolder.itsClusterSizes,
339 extraInfoHolder.tpcNClsFindable,
340 extraInfoHolder.tpcNClsFindableMinusFound,
341 extraInfoHolder.tpcNClsFindableMinusPID,
342 extraInfoHolder.tpcNClsFindableMinusCrossedRows,
343 extraInfoHolder.tpcNClsShared,
344 extraInfoHolder.trdPattern,
345 truncateFloatFraction(extraInfoHolder.itsChi2NCl, mTrackChi2),
346 truncateFloatFraction(extraInfoHolder.tpcChi2NCl, mTrackChi2),
347 truncateFloatFraction(extraInfoHolder.trdChi2, mTrackChi2),
348 truncateFloatFraction(extraInfoHolder.tofChi2, mTrackChi2),
349 truncateFloatFraction(extraInfoHolder.tpcSignal, mTrackSignal),
350 truncateFloatFraction(extraInfoHolder.trdSignal, mTrackSignal),
351 truncateFloatFraction(extraInfoHolder.length, mTrackSignal),
352 truncateFloatFraction(extraInfoHolder.tofExpMom, mTrack1Pt),
353 truncateFloatFraction(extraInfoHolder.trackEtaEMCAL, mTrackPosEMCAL),
354 truncateFloatFraction(extraInfoHolder.trackPhiEMCAL, mTrackPosEMCAL),
355 truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime),
356 trackTimeRes);
357}
358
359template <typename TracksQACursorType>
360void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder)
361{
362 tracksQACursor(
363 trackQAInfoHolder.trackID,
364 truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0),
365 trackQAInfoHolder.tpcdcaR,
366 trackQAInfoHolder.tpcdcaZ,
367 trackQAInfoHolder.tpcClusterByteMask,
368 trackQAInfoHolder.tpcdEdxMax0R,
369 trackQAInfoHolder.tpcdEdxMax1R,
370 trackQAInfoHolder.tpcdEdxMax2R,
371 trackQAInfoHolder.tpcdEdxMax3R,
372 trackQAInfoHolder.tpcdEdxTot0R,
373 trackQAInfoHolder.tpcdEdxTot1R,
374 trackQAInfoHolder.tpcdEdxTot2R,
375 trackQAInfoHolder.tpcdEdxTot3R,
376 trackQAInfoHolder.dRefContY,
377 trackQAInfoHolder.dRefContZ,
378 trackQAInfoHolder.dRefContSnp,
379 trackQAInfoHolder.dRefContTgl,
380 trackQAInfoHolder.dRefContQ2Pt,
381 trackQAInfoHolder.dRefGloY,
382 trackQAInfoHolder.dRefGloZ,
383 trackQAInfoHolder.dRefGloSnp,
384 trackQAInfoHolder.dRefGloTgl,
385 trackQAInfoHolder.dRefGloQ2Pt,
386 trackQAInfoHolder.dTofdX,
387 trackQAInfoHolder.dTofdZ);
388}
389
390template <typename mftTracksCursorType, typename AmbigMFTTracksCursorType>
391void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor,
392 GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID,
393 std::uint64_t collisionBC, const std::map<uint64_t, int>& bcsMap)
394{
395 // mft tracks
396 int bcSlice[2] = {-1, -1};
397 const auto& track = data.getMFTTrack(trackID);
398 const auto& rof = data.getMFTTracksROFRecords()[mMFTROFs[trackID.getIndex()]];
399 float trackTime = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mMFTROFrameHalfLengthNS + mMFTROFBiasNS;
400 float trackTimeRes = mMFTROFrameHalfLengthNS;
401 bool needBCSlice = collisionID < 0;
402 std::uint64_t bcOfTimeRef;
403 if (needBCSlice) {
404 double error = mTimeMarginTrackTime + trackTimeRes;
405 bcOfTimeRef = fillBCSlice(bcSlice, trackTime - error, trackTime + error, bcsMap);
406 } else {
407 bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default (unambiguous) track time is wrt collision BC
408 }
409 trackTime -= bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS;
410
411 // the Cellular Automaton track-finding algorithm flag is stored in first of the 4 bits not used for the cluster size
412 uint64_t mftClusterSizesAndTrackFlags = track.getClusterSizes();
413 mftClusterSizesAndTrackFlags |= (track.isCA()) ? (1ULL << (60)) : 0;
414
415 mftTracksCursor(collisionID,
416 track.getX(),
417 track.getY(),
418 truncateFloatFraction(track.getZ(), mTrackX), // for the forward tracks Z has the same role as X in barrel
419 truncateFloatFraction(track.getPhi(), mTrackAlpha),
420 truncateFloatFraction(track.getTanl(), mTrackTgl),
421 truncateFloatFraction(track.getInvQPt(), mTrack1Pt),
422 mftClusterSizesAndTrackFlags,
423 truncateFloatFraction(track.getTrackChi2(), mTrackChi2),
424 truncateFloatFraction(trackTime, mTrackTime),
425 truncateFloatFraction(trackTimeRes, mTrackTimeError));
426 if (needBCSlice) {
427 ambigMFTTracksCursor(mTableTrMFTID, bcSlice);
428 }
429}
430
431template <typename TracksCursorType, typename TracksCovCursorType, typename TracksExtraCursorType, typename TracksQACursorType, typename AmbigTracksCursorType,
432 typename MFTTracksCursorType, typename MFTTracksCovCursorType, typename AmbigMFTTracksCursorType,
433 typename FwdTracksCursorType, typename FwdTracksCovCursorType, typename AmbigFwdTracksCursorType, typename FwdTrkClsCursorType>
434void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID,
435 std::uint64_t collisionBC,
436 const o2::dataformats::VtxTrackRef& trackRef,
437 const gsl::span<const GIndex>& GIndices,
439 TracksCursorType& tracksCursor,
440 TracksCovCursorType& tracksCovCursor,
441 TracksExtraCursorType& tracksExtraCursor,
442 TracksQACursorType& tracksQACursor,
443 AmbigTracksCursorType& ambigTracksCursor,
444 MFTTracksCursorType& mftTracksCursor,
445 MFTTracksCovCursorType& mftTracksCovCursor,
446 AmbigMFTTracksCursorType& ambigMFTTracksCursor,
447 FwdTracksCursorType& fwdTracksCursor,
448 FwdTracksCovCursorType& fwdTracksCovCursor,
449 AmbigFwdTracksCursorType& ambigFwdTracksCursor,
450 FwdTrkClsCursorType& fwdTrkClsCursor,
451 const std::map<uint64_t, int>& bcsMap)
452{
453 for (int src = GIndex::NSources; src--;) {
454 if (!GIndex::isTrackSource(src)) {
455 continue;
456 }
457 int start = trackRef.getFirstEntryOfSource(src);
458 int end = start + trackRef.getEntriesOfSource(src);
459 int nToReserve = end - start; // + last index for a given table
460 if (src == GIndex::Source::MFT) {
461 mftTracksCursor.reserve(nToReserve + mftTracksCursor.lastIndex());
463 fwdTracksCursor.reserve(nToReserve + fwdTracksCursor.lastIndex());
464 fwdTracksCovCursor.reserve(nToReserve + fwdTracksCovCursor.lastIndex());
466 mftTracksCovCursor.reserve(nToReserve + mftTracksCovCursor.lastIndex());
467 }
468 } else {
469 tracksCursor.reserve(nToReserve + tracksCursor.lastIndex());
470 tracksCovCursor.reserve(nToReserve + tracksCovCursor.lastIndex());
471 tracksExtraCursor.reserve(nToReserve + tracksExtraCursor.lastIndex());
472 }
473 for (int ti = start; ti < end; ti++) {
474 const auto& trackIndex = GIndices[ti];
475 if (GIndex::includesSource(src, mInputSources)) {
476 if (src == GIndex::Source::MFT) { // MFT tracks are treated separately since they are stored in a different table
477 if (trackIndex.isAmbiguous() && mGIDToTableMFTID.find(trackIndex) != mGIDToTableMFTID.end()) { // was it already stored ?
478 continue;
479 }
480 addToMFTTracksTable(mftTracksCursor, ambigMFTTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap);
481 mGIDToTableMFTID.emplace(trackIndex, mTableTrMFTID);
482 mTableTrMFTID++;
483 } else if (src == GIndex::Source::MCH || src == GIndex::Source::MFTMCH || src == GIndex::Source::MCHMID) { // FwdTracks tracks are treated separately since they are stored in a different table
484 if (trackIndex.isAmbiguous() && mGIDToTableFwdID.find(trackIndex) != mGIDToTableFwdID.end()) { // was it already stored ?
485 continue;
486 }
487 addToFwdTracksTable(fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, mftTracksCovCursor, trackIndex, data, collisionID, collisionBC, bcsMap);
488 mGIDToTableFwdID.emplace(trackIndex, mTableTrFwdID);
489 addClustersToFwdTrkClsTable(data, fwdTrkClsCursor, trackIndex, mTableTrFwdID);
490 mTableTrFwdID++;
491 } else {
492 // barrel track: normal tracks table
493 if (trackIndex.isAmbiguous() && mGIDToTableID.find(trackIndex) != mGIDToTableID.end()) { // was it already stored ?
494 continue;
495 }
496
497 float weight = 0;
498 static std::uniform_real_distribution<> distr(0., 1.);
499 bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator));
500 auto extraInfoHolder = processBarrelTrack(collisionID, collisionBC, trackIndex, data, bcsMap);
501
502 if (writeQAData) {
503 auto trackQAInfoHolder = processBarrelTrackQA(collisionID, collisionBC, trackIndex, data, bcsMap);
504 if (std::bitset<8>(trackQAInfoHolder.tpcClusterByteMask).count() >= mTrackQCNTrCut) {
505 trackQAInfoHolder.trackID = mTableTrID;
506 // LOGP(info, "orig time0 in bc: {} diffBCRef: {}, ttime: {} -> {}", trackQAInfoHolder.tpcTime0*8, extraInfoHolder.diffBCRef, extraInfoHolder.trackTime, (trackQAInfoHolder.tpcTime0 * 8 - extraInfoHolder.diffBCRef) * o2::constants::lhc::LHCBunchSpacingNS - extraInfoHolder.trackTime);
507 trackQAInfoHolder.tpcTime0 = (trackQAInfoHolder.tpcTime0 * 8 - extraInfoHolder.diffBCRef) * o2::constants::lhc::LHCBunchSpacingNS - extraInfoHolder.trackTime;
508 // difference between TPC track time0 and stored track nominal time in ns instead of TF start
509 addToTracksQATable(tracksQACursor, trackQAInfoHolder);
510 } else {
511 writeQAData = false;
512 }
513 }
514
515 if (mThinTracks && src == GIndex::Source::TPC && mGIDUsedBySVtx.find(trackIndex) == mGIDUsedBySVtx.end() && mGIDUsedByStr.find(trackIndex) == mGIDUsedByStr.end() && !writeQAData) {
516 mGIDToTableID.emplace(trackIndex, -1); // pretend skipped tracks are stored; this is safe since they are are not written to disk and -1 indicates to all users to not use this track
517 continue;
518 }
519
520 if (!extraInfoHolder.isTPConly && extraInfoHolder.trackTimeRes < 0.f) { // failed or rejected?
521 LOG(warning) << "Barrel track " << trackIndex << " has no time set, rejection is not expected : time=" << extraInfoHolder.trackTime
522 << " timeErr=" << extraInfoHolder.trackTimeRes << " BCSlice: " << extraInfoHolder.bcSlice[0] << ":" << extraInfoHolder.bcSlice[1];
523 continue;
524 }
525 const auto& trOrig = data.getTrackParam(trackIndex);
526 bool isProp = false;
527 if (mPropTracks && trOrig.getX() < mMinPropR &&
528 mGIDUsedBySVtx.find(trackIndex) == mGIDUsedBySVtx.end() &&
529 mGIDUsedByStr.find(trackIndex) == mGIDUsedByStr.end()) { // Do not propagate track assoc. to V0s and str. tracking
530 auto trackPar(trOrig);
531 isProp = propagateTrackToPV(trackPar, data, collisionID);
532 if (isProp) {
533 addToTracksTable(tracksCursor, tracksCovCursor, trackPar, collisionID, aod::track::Track);
534 }
535 }
536 if (!isProp) {
537 addToTracksTable(tracksCursor, tracksCovCursor, trOrig, collisionID, aod::track::TrackIU);
538 }
539 addToTracksExtraTable(tracksExtraCursor, extraInfoHolder);
540
541 // collecting table indices of barrel tracks for V0s table
542 if (extraInfoHolder.bcSlice[0] >= 0 && collisionID < 0) {
543 ambigTracksCursor(mTableTrID, extraInfoHolder.bcSlice);
544 }
545 mGIDToTableID.emplace(trackIndex, mTableTrID);
546 mTableTrID++;
547 }
548 }
549 }
550 }
551 if (collisionID < 0) {
552 return;
553 }
555 auto sTracks = data.getStrangeTracks();
556 tracksCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksCursor.lastIndex());
557 tracksCovCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksCovCursor.lastIndex());
558 tracksExtraCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksExtraCursor.lastIndex());
559 for (int iS{mVertexStrLUT[collisionID]}; iS < mVertexStrLUT[collisionID + 1]; ++iS) {
560 auto& collStrTrk = mCollisionStrTrk[iS];
561 auto& sTrk = sTracks[collStrTrk.second];
562 TrackExtraInfo extraInfo;
563 extraInfo.itsChi2NCl = sTrk.mTopoChi2; // TODO: this is the total chi2 of adding the ITS clusters, the topology chi2 meaning might change in the future
564 extraInfo.itsClusterSizes = sTrk.getClusterSizes();
565 addToTracksTable(tracksCursor, tracksCovCursor, sTrk.mMother, collisionID, aod::track::StrangeTrack);
566 addToTracksExtraTable(tracksExtraCursor, extraInfo);
567 mStrTrkIndices[collStrTrk.second] = mTableTrID;
568 mTableTrID++;
569 }
570}
571
572void AODProducerWorkflowDPL::fillIndexTablesPerCollision(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span<const GIndex>& GIndices, const o2::globaltracking::RecoContainer& data)
573{
574 const auto& mchmidMatches = data.getMCHMIDMatches();
575
577 int start = trackRef.getFirstEntryOfSource(src);
578 int end = start + trackRef.getEntriesOfSource(src);
579 for (int ti = start; ti < end; ti++) {
580 auto& trackIndex = GIndices[ti];
581 if (GIndex::includesSource(src, mInputSources)) {
582 if (src == GIndex::Source::MFT) {
583 if (trackIndex.isAmbiguous() && mGIDToTableMFTID.find(trackIndex) != mGIDToTableMFTID.end()) {
584 continue;
585 }
586 mGIDToTableMFTID.emplace(trackIndex, mIndexMFTID);
587 mIndexTableMFT[trackIndex.getIndex()] = mIndexMFTID;
588 mIndexMFTID++;
590 if (trackIndex.isAmbiguous() && mGIDToTableFwdID.find(trackIndex) != mGIDToTableFwdID.end()) {
591 continue;
592 }
593 mGIDToTableFwdID.emplace(trackIndex, mIndexFwdID);
594 if (src == GIndex::Source::MCH) {
595 mIndexTableFwd[trackIndex.getIndex()] = mIndexFwdID;
596 } else if (src == GIndex::Source::MCHMID) {
597 const auto& mchmidMatch = mchmidMatches[trackIndex.getIndex()];
598 const auto mchTrackID = mchmidMatch.getMCHRef().getIndex();
599 mIndexTableFwd[mchTrackID] = mIndexFwdID;
600 }
601 mIndexFwdID++;
602 }
603 }
604 }
605 }
606}
607
608template <typename FwdTracksCursorType, typename FwdTracksCovCursorType, typename AmbigFwdTracksCursorType, typename mftTracksCovCursorType>
609void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor,
610 AmbigFwdTracksCursorType& ambigFwdTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, GIndex trackID,
611 const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC,
612 const std::map<uint64_t, int>& bcsMap)
613{
614 const auto& mchTracks = data.getMCHTracks();
615 const auto& midTracks = data.getMIDTracks();
616 const auto& mchmidMatches = data.getMCHMIDMatches();
617 const auto& mchClusters = data.getMCHTrackClusters();
618
619 FwdTrackInfo fwdInfo;
620 FwdTrackCovInfo fwdCovInfo;
621 int bcSlice[2] = {-1, -1};
622
623 // helper lambda for mch bitmap -- common for global and standalone tracks
624 auto getMCHBitMap = [&](int mchTrackID) {
625 if (mchTrackID != -1) { // check matching just in case
626 const auto& mchTrack = mchTracks[mchTrackID];
627 int first = mchTrack.getFirstClusterIdx();
628 int last = mchTrack.getLastClusterIdx();
629 for (int i = first; i <= last; i++) { // check chamberIds of all clusters
630 const auto& cluster = mchClusters[i];
631 int chamberId = cluster.getChamberId();
632 fwdInfo.mchBitMap |= 1 << chamberId;
633 }
634 }
635 };
636
637 auto getMIDBitMapBoards = [&](int midTrackID) {
638 if (midTrackID != -1) { // check matching just in case
639 const auto& midTrack = midTracks[midTrackID];
640 fwdInfo.midBitMap = midTrack.getHitMap();
641 fwdInfo.midBoards = midTrack.getEfficiencyWord();
642 }
643 };
644
645 auto extrapMCHTrack = [&](int mchTrackID) {
646 const auto& track = mchTracks[mchTrackID];
647
648 // mch standalone tracks extrapolated to vertex
649 // compute 3 sets of tracks parameters :
650 // - at vertex
651 // - at DCA
652 // - at the end of the absorber
653 // extrapolate to vertex
654 float vx = 0, vy = 0, vz = 0;
655 if (collisionID >= 0) {
656 const auto& v = data.getPrimaryVertex(collisionID);
657 vx = v.getX();
658 vy = v.getY();
659 vz = v.getZ();
660 }
661
662 o2::mch::TrackParam trackParamAtVertex(track.getZ(), track.getParameters(), track.getCovariances());
663 if (mPropMuons) {
664 double errVtx{0.0}; // FIXME: get errors associated with vertex if available
665 double errVty{0.0};
666 if (!o2::mch::TrackExtrap::extrapToVertex(trackParamAtVertex, vx, vy, vz, errVtx, errVty)) {
667 return false;
668 }
669 }
670
671 // extrapolate to DCA
672 o2::mch::TrackParam trackParamAtDCA(track.getZ(), track.getParameters());
673 if (!o2::mch::TrackExtrap::extrapToVertexWithoutBranson(trackParamAtDCA, vz)) {
674 return false;
675 }
676
677 // extrapolate to the end of the absorber
678 o2::mch::TrackParam trackParamAtRAbs(track.getZ(), track.getParameters());
679 if (!o2::mch::TrackExtrap::extrapToZ(trackParamAtRAbs, -505.)) { // FIXME: replace hardcoded 505
680 return false;
681 }
682
683 double dcaX = trackParamAtDCA.getNonBendingCoor() - vx;
684 double dcaY = trackParamAtDCA.getBendingCoor() - vy;
685 double dca = std::sqrt(dcaX * dcaX + dcaY * dcaY);
686
687 double xAbs = trackParamAtRAbs.getNonBendingCoor();
688 double yAbs = trackParamAtRAbs.getBendingCoor();
689
690 double dpdca = track.getP() * dca;
691 double dchi2 = track.getChi2OverNDF();
692
693 auto fwdmuon = mMatching.MCHtoFwd(trackParamAtVertex);
694
695 fwdInfo.x = fwdmuon.getX();
696 fwdInfo.y = fwdmuon.getY();
697 fwdInfo.z = fwdmuon.getZ();
698 fwdInfo.phi = fwdmuon.getPhi();
699 fwdInfo.tanl = fwdmuon.getTgl();
700 fwdInfo.invqpt = fwdmuon.getInvQPt();
701 fwdInfo.rabs = std::sqrt(xAbs * xAbs + yAbs * yAbs);
702 fwdInfo.chi2 = dchi2;
703 fwdInfo.pdca = dpdca;
704 fwdInfo.nClusters = track.getNClusters();
705
706 fwdCovInfo.sigX = TMath::Sqrt(fwdmuon.getCovariances()(0, 0));
707 fwdCovInfo.sigY = TMath::Sqrt(fwdmuon.getCovariances()(1, 1));
708 fwdCovInfo.sigPhi = TMath::Sqrt(fwdmuon.getCovariances()(2, 2));
709 fwdCovInfo.sigTgl = TMath::Sqrt(fwdmuon.getCovariances()(3, 3));
710 fwdCovInfo.sig1Pt = TMath::Sqrt(fwdmuon.getCovariances()(4, 4));
711 fwdCovInfo.rhoXY = (Char_t)(128. * fwdmuon.getCovariances()(0, 1) / (fwdCovInfo.sigX * fwdCovInfo.sigY));
712 fwdCovInfo.rhoPhiX = (Char_t)(128. * fwdmuon.getCovariances()(0, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigX));
713 fwdCovInfo.rhoPhiY = (Char_t)(128. * fwdmuon.getCovariances()(1, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigY));
714 fwdCovInfo.rhoTglX = (Char_t)(128. * fwdmuon.getCovariances()(0, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigX));
715 fwdCovInfo.rhoTglY = (Char_t)(128. * fwdmuon.getCovariances()(1, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigY));
716 fwdCovInfo.rhoTglPhi = (Char_t)(128. * fwdmuon.getCovariances()(2, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigPhi));
717 fwdCovInfo.rho1PtX = (Char_t)(128. * fwdmuon.getCovariances()(0, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigX));
718 fwdCovInfo.rho1PtY = (Char_t)(128. * fwdmuon.getCovariances()(1, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigY));
719 fwdCovInfo.rho1PtPhi = (Char_t)(128. * fwdmuon.getCovariances()(2, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigPhi));
720 fwdCovInfo.rho1PtTgl = (Char_t)(128. * fwdmuon.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl));
721
722 return true;
723 };
724
725 if (trackID.getSource() == GIndex::MCH) { // This is an MCH track
726 int mchTrackID = trackID.getIndex();
727 getMCHBitMap(mchTrackID);
728 if (!extrapMCHTrack(mchTrackID)) {
729 LOGF(warn, "Unable to extrapolate MCH track with ID %d! Dummy parameters will be used", mchTrackID);
730 }
731 fwdInfo.trackTypeId = o2::aod::fwdtrack::MCHStandaloneTrack;
732 const auto& rof = data.getMCHTracksROFRecords()[mMCHROFs[mchTrackID]];
733 auto time = rof.getTimeMUS(mStartIR).first;
734 fwdInfo.trackTime = time.getTimeStamp() * 1.e3;
735 fwdInfo.trackTimeRes = time.getTimeStampError() * 1.e3;
736 } else if (trackID.getSource() == GIndex::MCHMID) { // This is an MCH-MID track
737 fwdInfo.trackTypeId = o2::aod::fwdtrack::MuonStandaloneTrack;
738 auto mchmidMatch = mchmidMatches[trackID.getIndex()];
739 auto mchTrackID = mchmidMatch.getMCHRef().getIndex();
740 if (!extrapMCHTrack(mchTrackID)) {
741 LOGF(warn, "Unable to extrapolate MCH track with ID %d! Dummy parameters will be used", mchTrackID);
742 }
743 auto midTrackID = mchmidMatch.getMIDRef().getIndex();
744 fwdInfo.chi2matchmchmid = mchmidMatch.getMatchChi2OverNDF();
745 getMCHBitMap(mchTrackID);
746 getMIDBitMapBoards(midTrackID);
747 auto time = mchmidMatch.getTimeMUS(mStartIR).first;
748 fwdInfo.trackTime = time.getTimeStamp() * 1.e3;
749 fwdInfo.trackTimeRes = time.getTimeStampError() * 1.e3;
750 } else { // This is a GlobalMuonTrack or a GlobalForwardTrack
751 const auto& track = data.getGlobalFwdTrack(trackID);
752 const auto& mftTracks = data.getMFTTracks();
753 const auto& mfttrack = mftTracks[track.getMFTTrackID()];
754 if (!extrapMCHTrack(track.getMCHTrackID())) {
755 LOGF(warn, "Unable to extrapolate MCH track with ID %d! Dummy parameters will be used", track.getMCHTrackID());
756 }
757 fwdInfo.x = track.getX();
758 fwdInfo.y = track.getY();
759 fwdInfo.z = track.getZ();
760 fwdInfo.phi = track.getPhi();
761 fwdInfo.tanl = track.getTanl();
762 fwdInfo.invqpt = track.getInvQPt();
763 fwdInfo.chi2 = track.getTrackChi2();
764 // fwdInfo.nClusters = track.getNumberOfPoints();
765 fwdInfo.chi2matchmchmid = track.getMIDMatchingChi2();
766 fwdInfo.chi2matchmchmft = track.getMFTMCHMatchingChi2();
767 fwdInfo.matchscoremchmft = track.getMFTMCHMatchingScore();
768 fwdInfo.matchmfttrackid = mIndexTableMFT[track.getMFTTrackID()];
769 fwdInfo.matchmchtrackid = mIndexTableFwd[track.getMCHTrackID()];
770 fwdInfo.trackTime = track.getTimeMUS().getTimeStamp() * 1.e3;
771 fwdInfo.trackTimeRes = track.getTimeMUS().getTimeStampError() * 1.e3;
772
773 getMCHBitMap(track.getMCHTrackID());
774 getMIDBitMapBoards(track.getMIDTrackID());
775
776 fwdCovInfo.sigX = TMath::Sqrt(track.getCovariances()(0, 0));
777 fwdCovInfo.sigY = TMath::Sqrt(track.getCovariances()(1, 1));
778 fwdCovInfo.sigPhi = TMath::Sqrt(track.getCovariances()(2, 2));
779 fwdCovInfo.sigTgl = TMath::Sqrt(track.getCovariances()(3, 3));
780 fwdCovInfo.sig1Pt = TMath::Sqrt(track.getCovariances()(4, 4));
781 fwdCovInfo.rhoXY = (Char_t)(128. * track.getCovariances()(0, 1) / (fwdCovInfo.sigX * fwdCovInfo.sigY));
782 fwdCovInfo.rhoPhiX = (Char_t)(128. * track.getCovariances()(0, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigX));
783 fwdCovInfo.rhoPhiY = (Char_t)(128. * track.getCovariances()(1, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigY));
784 fwdCovInfo.rhoTglX = (Char_t)(128. * track.getCovariances()(0, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigX));
785 fwdCovInfo.rhoTglY = (Char_t)(128. * track.getCovariances()(1, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigY));
786 fwdCovInfo.rhoTglPhi = (Char_t)(128. * track.getCovariances()(2, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigPhi));
787 fwdCovInfo.rho1PtX = (Char_t)(128. * track.getCovariances()(0, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigX));
788 fwdCovInfo.rho1PtY = (Char_t)(128. * track.getCovariances()(1, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigY));
789 fwdCovInfo.rho1PtPhi = (Char_t)(128. * track.getCovariances()(2, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigPhi));
790 fwdCovInfo.rho1PtTgl = (Char_t)(128. * track.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl));
791
792 fwdInfo.trackTypeId = (fwdInfo.chi2matchmchmid >= 0) ? o2::aod::fwdtrack::GlobalMuonTrack : o2::aod::fwdtrack::GlobalForwardTrack;
793
794 float sX = TMath::Sqrt(mfttrack.getSigma2X()), sY = TMath::Sqrt(mfttrack.getSigma2Y()), sPhi = TMath::Sqrt(mfttrack.getSigma2Phi()),
795 sTgl = TMath::Sqrt(mfttrack.getSigma2Tanl()), sQ2Pt = TMath::Sqrt(mfttrack.getSigma2InvQPt());
796
797 mftTracksCovCursor(fwdInfo.matchmfttrackid,
798 truncateFloatFraction(sX, mTrackCovDiag),
799 truncateFloatFraction(sY, mTrackCovDiag),
800 truncateFloatFraction(sPhi, mTrackCovDiag),
801 truncateFloatFraction(sTgl, mTrackCovDiag),
802 truncateFloatFraction(sQ2Pt, mTrackCovDiag),
803 (Char_t)(128. * mfttrack.getCovariances()(0, 1) / (sX * sY)),
804 (Char_t)(128. * mfttrack.getCovariances()(0, 2) / (sPhi * sX)),
805 (Char_t)(128. * mfttrack.getCovariances()(1, 2) / (sPhi * sY)),
806 (Char_t)(128. * mfttrack.getCovariances()(0, 3) / (sTgl * sX)),
807 (Char_t)(128. * mfttrack.getCovariances()(1, 3) / (sTgl * sY)),
808 (Char_t)(128. * mfttrack.getCovariances()(2, 3) / (sTgl * sPhi)),
809 (Char_t)(128. * mfttrack.getCovariances()(0, 4) / (sQ2Pt * sX)),
810 (Char_t)(128. * mfttrack.getCovariances()(1, 4) / (sQ2Pt * sY)),
811 (Char_t)(128. * mfttrack.getCovariances()(2, 4) / (sQ2Pt * sPhi)),
812 (Char_t)(128. * mfttrack.getCovariances()(3, 4) / (sQ2Pt * sTgl)));
813 }
814
815 std::uint64_t bcOfTimeRef;
816 bool needBCSlice = collisionID < 0;
817 if (needBCSlice) { // need to store BC slice
818 float err = mTimeMarginTrackTime + fwdInfo.trackTimeRes;
819 bcOfTimeRef = fillBCSlice(bcSlice, fwdInfo.trackTime - err, fwdInfo.trackTime + err, bcsMap);
820 } else {
821 bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default track time is wrt collision BC (unless no collision assigned)
822 }
823 fwdInfo.trackTime -= bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS;
824
825 fwdTracksCursor(collisionID,
826 fwdInfo.trackTypeId,
827 fwdInfo.x,
828 fwdInfo.y,
829 truncateFloatFraction(fwdInfo.z, mTrackX), // for the forward tracks Z has the same role as X in the barrel
830 truncateFloatFraction(fwdInfo.phi, mTrackAlpha),
831 truncateFloatFraction(fwdInfo.tanl, mTrackTgl),
832 truncateFloatFraction(fwdInfo.invqpt, mTrack1Pt),
833 fwdInfo.nClusters,
834 truncateFloatFraction(fwdInfo.pdca, mTrackX),
835 truncateFloatFraction(fwdInfo.rabs, mTrackX),
836 truncateFloatFraction(fwdInfo.chi2, mTrackChi2),
837 truncateFloatFraction(fwdInfo.chi2matchmchmid, mTrackChi2),
838 truncateFloatFraction(fwdInfo.chi2matchmchmft, mTrackChi2),
839 truncateFloatFraction(fwdInfo.matchscoremchmft, mTrackChi2),
840 fwdInfo.matchmfttrackid,
841 fwdInfo.matchmchtrackid,
842 fwdInfo.mchBitMap,
843 fwdInfo.midBitMap,
844 fwdInfo.midBoards,
845 truncateFloatFraction(fwdInfo.trackTime, mTrackTime),
846 truncateFloatFraction(fwdInfo.trackTimeRes, mTrackTimeError));
847
848 fwdTracksCovCursor(truncateFloatFraction(fwdCovInfo.sigX, mTrackCovDiag),
849 truncateFloatFraction(fwdCovInfo.sigY, mTrackCovDiag),
850 truncateFloatFraction(fwdCovInfo.sigPhi, mTrackCovDiag),
851 truncateFloatFraction(fwdCovInfo.sigTgl, mTrackCovDiag),
852 truncateFloatFraction(fwdCovInfo.sig1Pt, mTrackCovDiag),
853 fwdCovInfo.rhoXY,
854 fwdCovInfo.rhoPhiX,
855 fwdCovInfo.rhoPhiY,
856 fwdCovInfo.rhoTglX,
857 fwdCovInfo.rhoTglY,
858 fwdCovInfo.rhoTglPhi,
859 fwdCovInfo.rho1PtX,
860 fwdCovInfo.rho1PtY,
861 fwdCovInfo.rho1PtPhi,
862 fwdCovInfo.rho1PtTgl);
863
864 if (needBCSlice) {
865 ambigFwdTracksCursor(mTableTrFwdID, bcSlice);
866 }
867}
868
869//------------------------------------------------------------------
870void AODProducerWorkflowDPL::updateMCHeader(MCCollisionCursor& collisionCursor,
871 XSectionCursor& xSectionCursor,
872 PdfInfoCursor& pdfInfoCursor,
873 HeavyIonCursor& heavyIonCursor,
874 const MCEventHeader& header,
875 int collisionID,
876 int bcID,
877 float time,
878 short generatorID,
879 int sourceID)
880{
885
886 auto genID = updateMCCollisions(collisionCursor,
887 bcID,
888 time,
889 header,
890 generatorID,
891 sourceID,
892 mCollisionPosition);
893 mXSectionUpdate = (updateHepMCXSection(xSectionCursor, //
894 collisionID, //
895 genID, //
896 header, //
897 mXSectionUpdate)
898 ? HepMCUpdate::always
899 : HepMCUpdate::never);
900 mPdfInfoUpdate = (updateHepMCPdfInfo(pdfInfoCursor, //
901 collisionID, //
902 genID, //
903 header, //
904 mPdfInfoUpdate)
905 ? HepMCUpdate::always
906 : HepMCUpdate::never);
907 mHeavyIonUpdate = (updateHepMCHeavyIon(heavyIonCursor, //
908 collisionID, //
909 genID, //
910 header,
911 mHeavyIonUpdate)
912 ? HepMCUpdate::always
913 : HepMCUpdate::never);
914}
915
916void dimensionMCKeepStore(std::vector<std::vector<std::unordered_map<int, int>>>& store, int Nsources, int NEvents)
917{
918 store.resize(Nsources);
919 for (int s = 0; s < Nsources; ++s) {
920 store[s].resize(NEvents);
921 }
922}
923
924void clearMCKeepStore(std::vector<std::vector<std::unordered_map<int, int>>>& store)
925{
926 for (auto s = 0U; s < store.size(); ++s) {
927 for (auto e = 0U; e < store[s].size(); ++e) {
928 store[s][e].clear();
929 }
930 }
931}
932
933// helper function to add a particle/track to the MC keep store
934void keepMCParticle(std::vector<std::vector<std::unordered_map<int, int>>>& store, int source, int event, int track, int value = 1)
935{
936 if (track < 0) {
937 LOG(warn) << "trackID is smaller than 0. Neglecting";
938 return;
939 }
940 store[source][event][track] = value;
941}
942
943void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader,
944 MCParticlesCursor& mcParticlesCursor,
945 const gsl::span<const o2::dataformats::VtxTrackRef>& primVer2TRefs,
946 const gsl::span<const GIndex>& GIndices,
948 const std::vector<std::vector<int>>& mcColToEvSrc)
949{
950 int NSources = 0;
951 int NEvents = 0;
952 for (auto& p : mcColToEvSrc) {
953 NSources = std::max(p[1], NSources);
954 NEvents = std::max(p[2], NEvents);
955 }
956 NSources++; // 0 - indexed
957 NEvents++;
958 LOG(info) << " number of events " << NEvents;
959 LOG(info) << " number of sources " << NSources;
960 dimensionMCKeepStore(mToStore, NSources, NEvents);
961
962 std::vector<int> particleIDsToKeep;
963
964 auto markMCTrackForSrc = [&](std::array<GID, GID::NSources>& contributorsGID, uint8_t src) {
965 auto mcLabel = data.getTrackMCLabel(contributorsGID[src]);
966 if (!mcLabel.isValid()) {
967 return;
968 }
969 keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID());
970 };
971
972 // mark reconstructed MC particles to store them into the table
973 for (auto& trackRef : primVer2TRefs) {
974 for (int src = GIndex::NSources; src--;) {
975 int start = trackRef.getFirstEntryOfSource(src);
976 int end = start + trackRef.getEntriesOfSource(src);
977 for (int ti = start; ti < end; ti++) {
978 auto& trackIndex = GIndices[ti];
979 if (GIndex::includesSource(src, mInputSources)) {
980 auto mcTruth = data.getTrackMCLabel(trackIndex);
981 if (!mcTruth.isValid()) {
982 continue;
983 }
984 keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID());
985 // treating contributors of global tracks
986 auto contributorsGID = data.getSingleDetectorRefs(trackIndex);
987 if (contributorsGID[GIndex::Source::TPC].isIndexSet()) {
988 markMCTrackForSrc(contributorsGID, GIndex::Source::TPC);
989 }
990 if (contributorsGID[GIndex::Source::ITS].isIndexSet()) {
991 markMCTrackForSrc(contributorsGID, GIndex::Source::ITS);
992 }
993 if (contributorsGID[GIndex::Source::TOF].isIndexSet()) {
994 const auto& labelsTOF = data.getTOFClustersMCLabels()->getLabels(contributorsGID[GIndex::Source::TOF]);
995 for (auto& mcLabel : labelsTOF) {
996 if (!mcLabel.isValid()) {
997 continue;
998 }
999 keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID());
1000 }
1001 }
1002 }
1003 }
1004 }
1005 }
1006 // mark calorimeter signals as reconstructed particles
1007 if (mInputSources[GIndex::EMC]) {
1008 auto& mcCaloEMCCellLabels = data.getEMCALCellsMCLabels()->getTruthArray();
1009 for (auto& mcTruth : mcCaloEMCCellLabels) {
1010 if (!mcTruth.isValid()) {
1011 continue;
1012 }
1013 keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID());
1014 }
1015 }
1016 if (mInputSources[GIndex::PHS]) {
1017 auto& mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels()->getTruthArray();
1018 for (auto& mcTruth : mcCaloPHOSCellLabels) {
1019 if (!mcTruth.isValid()) {
1020 continue;
1021 }
1022 keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID());
1023 }
1024 }
1025 using namespace aodmchelpers;
1026 using MCTrackNavigator = o2::mcutils::MCTrackNavigator;
1027
1028 size_t offset = 0;
1029 for (auto& colInfo : mcColToEvSrc) { // loop over "<eventID, sourceID> <-> combined MC col. ID" key pairs
1030 int event = colInfo[2];
1031 int source = colInfo[1];
1032 int mcColId = colInfo[0];
1033 std::vector<MCTrack> const& mcParticles = mcReader.getTracks(source, event);
1034 LOG(debug) << "Event=" << event << " source=" << source << " collision=" << mcColId;
1035 auto& preselect = mToStore[source][event];
1036
1037 offset = updateParticles(mcParticlesCursor,
1038 mcColId,
1039 mcParticles,
1040 preselect,
1041 offset,
1042 mRecoOnly,
1043 source == 0, // background
1044 mMcParticleW,
1045 mMcParticleMom,
1046 mMcParticlePos);
1047
1049 }
1050}
1051
1052template <typename MCTrackLabelCursorType, typename MCMFTTrackLabelCursorType, typename MCFwdTrackLabelCursorType>
1053void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor,
1054 MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor,
1055 MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor,
1056 const o2::dataformats::VtxTrackRef& trackRef,
1057 const gsl::span<const GIndex>& primVerGIs,
1059 int vertexId)
1060{
1061 // labelMask (temporary) usage:
1062 // bit 13 -- ITS/TPC with ITS label (track of AB tracklet) different from TPC
1063 // bit 14 -- isNoise() == true
1064 // bit 15 -- isFake() == true (defined by the fakeness of the top level global track, i.e. if TOF is present, fake means that the track of the TPC label does not contribute to TOF cluster)
1065 // labelID = -1 -- label is not set
1066
1067 for (int src = GIndex::NSources; src--;) {
1068 int start = trackRef.getFirstEntryOfSource(src);
1069 int end = start + trackRef.getEntriesOfSource(src);
1070 mcMFTTrackLabelCursor.reserve(end - start + mcMFTTrackLabelCursor.lastIndex());
1071 mcFwdTrackLabelCursor.reserve(end - start + mcFwdTrackLabelCursor.lastIndex());
1072 mcTrackLabelCursor.reserve(end - start + mcTrackLabelCursor.lastIndex());
1073 for (int ti = start; ti < end; ti++) {
1074 const auto trackIndex = primVerGIs[ti];
1075
1076 // check if the label was already stored (or the track was rejected for some reason in the fillTrackTablesPerCollision)
1077 auto needToStore = [trackIndex](std::unordered_map<GIndex, int>& mp) {
1078 auto entry = mp.find(trackIndex);
1079 if (entry == mp.end() || entry->second == -1) {
1080 return false;
1081 }
1082 entry->second = -1;
1083 return true;
1084 };
1085
1086 if (GIndex::includesSource(src, mInputSources)) {
1087 auto mcTruth = data.getTrackMCLabel(trackIndex);
1088 MCLabels labelHolder{};
1089 if ((src == GIndex::Source::MFT) || (src == GIndex::Source::MFTMCH) || (src == GIndex::Source::MCH) || (src == GIndex::Source::MCHMID)) { // treating mft and fwd labels separately
1090 if (!needToStore(src == GIndex::Source::MFT ? mGIDToTableMFTID : mGIDToTableFwdID)) {
1091 continue;
1092 }
1093 if (mcTruth.isValid()) { // if not set, -1 will be stored
1094 labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()];
1095 }
1096 if (mcTruth.isFake()) {
1097 labelHolder.fwdLabelMask |= (0x1 << 7);
1098 }
1099 if (mcTruth.isNoise()) {
1100 labelHolder.fwdLabelMask |= (0x1 << 6);
1101 }
1102 if (src == GIndex::Source::MFT) {
1103 mcMFTTrackLabelCursor(labelHolder.labelID,
1104 labelHolder.fwdLabelMask);
1105 } else {
1106 mcFwdTrackLabelCursor(labelHolder.labelID,
1107 labelHolder.fwdLabelMask);
1108 }
1109 } else {
1110 if (!needToStore(mGIDToTableID)) {
1111 continue;
1112 }
1113 if (mcTruth.isValid()) { // if not set, -1 will be stored
1114 labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; // defined by TPC if it contributes, otherwise: by ITS
1115 if (mcTruth.isFake()) {
1116 labelHolder.labelMask |= (0x1 << 15);
1117 }
1118 if (trackIndex.includesDet(DetID::TPC) && trackIndex.getSource() != GIndex::Source::TPC) { // this is global track
1119 auto contributorsGID = data.getSingleDetectorRefs(trackIndex);
1120 if (contributorsGID[GIndex::Source::ITSTPC].isIndexSet()) { // there is a match to ITS tracks or ITSAB tracklet!
1121 if (data.getTrackMCLabel(contributorsGID[GIndex::Source::ITSTPC]).isFake()) {
1122 labelHolder.labelMask |= (0x1 << 13);
1123 }
1124 }
1125 }
1126 } else if (mcTruth.isNoise()) {
1127 labelHolder.labelMask |= (0x1 << 14);
1128 }
1129 mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask);
1130 }
1131 }
1132 }
1133 }
1134
1135 // filling the tables with the strangeness tracking labels
1136 auto sTrackLabels = data.getStrangeTracksMCLabels();
1137 // check if vertexId and vertexId + 1 maps into mVertexStrLUT
1138 if (!(vertexId < 0 || vertexId >= mVertexStrLUT.size() - 1)) {
1139 mcTrackLabelCursor.reserve(mVertexStrLUT[vertexId + 1] + mcTrackLabelCursor.lastIndex());
1140 for (int iS{mVertexStrLUT[vertexId]}; iS < mVertexStrLUT[vertexId + 1]; ++iS) {
1141 auto& collStrTrk = mCollisionStrTrk[iS];
1142 auto& label = sTrackLabels[collStrTrk.second];
1143 MCLabels labelHolder;
1144 labelHolder.labelID = label.isValid() ? (mToStore[label.getSourceID()][label.getEventID()])[label.getTrackID()] : -1;
1145 labelHolder.labelMask = (label.isFake() << 15) | (label.isNoise() << 14);
1146 mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask);
1147 }
1148 }
1149}
1150
1151template <typename V0CursorType, typename CascadeCursorType, typename Decay3BodyCursorType>
1152void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::RecoContainer& recoData, V0CursorType& v0Cursor, CascadeCursorType& cascadeCursor, Decay3BodyCursorType& decay3BodyCursor)
1153{
1154
1155 auto v0s = recoData.getV0sIdx();
1156 auto cascades = recoData.getCascadesIdx();
1157 auto decays3Body = recoData.getDecays3BodyIdx();
1158
1159 v0Cursor.reserve(v0s.size());
1160 // filling v0s table
1161 for (size_t iv0 = 0; iv0 < v0s.size(); iv0++) {
1162 const auto& v0 = v0s[iv0];
1163 auto trPosID = v0.getProngID(0);
1164 auto trNegID = v0.getProngID(1);
1165 uint8_t v0flags = v0.getBits();
1166 int posTableIdx = -1, negTableIdx = -1, collID = -1;
1167 auto item = mGIDToTableID.find(trPosID);
1168 if (item != mGIDToTableID.end()) {
1169 posTableIdx = item->second;
1170 } else {
1171 LOG(warn) << "Could not find a positive track index for prong ID " << trPosID;
1172 }
1173 item = mGIDToTableID.find(trNegID);
1174 if (item != mGIDToTableID.end()) {
1175 negTableIdx = item->second;
1176 } else {
1177 LOG(warn) << "Could not find a negative track index for prong ID " << trNegID;
1178 }
1179 auto itemV = mVtxToTableCollID.find(v0.getVertexID());
1180 if (itemV == mVtxToTableCollID.end()) {
1181 LOG(warn) << "Could not find V0 collisionID for the vertex ID " << v0.getVertexID();
1182 } else {
1183 collID = itemV->second;
1184 }
1185 if (posTableIdx != -1 and negTableIdx != -1 and collID != -1) {
1186 v0Cursor(collID, posTableIdx, negTableIdx, v0flags);
1187 mV0ToTableID[int(iv0)] = mTableV0ID++;
1188 }
1189 }
1190
1191 // filling cascades table
1192 cascadeCursor.reserve(cascades.size());
1193 for (auto& cascade : cascades) {
1194 auto itemV0 = mV0ToTableID.find(cascade.getV0ID());
1195 if (itemV0 == mV0ToTableID.end()) {
1196 continue;
1197 }
1198 int v0tableID = itemV0->second, bachTableIdx = -1, collID = -1;
1199 auto bachelorID = cascade.getBachelorID();
1200 auto item = mGIDToTableID.find(bachelorID);
1201 if (item != mGIDToTableID.end()) {
1202 bachTableIdx = item->second;
1203 } else {
1204 LOG(warn) << "Could not find a bachelor track index";
1205 continue;
1206 }
1207 auto itemV = mVtxToTableCollID.find(cascade.getVertexID());
1208 if (itemV != mVtxToTableCollID.end()) {
1209 collID = itemV->second;
1210 } else {
1211 LOG(warn) << "Could not find cascade collisionID for the vertex ID " << cascade.getVertexID();
1212 continue;
1213 }
1214 cascadeCursor(collID, v0tableID, bachTableIdx);
1215 }
1216
1217 // filling 3 body decays table
1218 decay3BodyCursor.reserve(decays3Body.size());
1219 for (size_t i3Body = 0; i3Body < decays3Body.size(); i3Body++) {
1220 const auto& decay3Body = decays3Body[i3Body];
1221 GIndex trIDs[3]{
1222 decay3Body.getProngID(0),
1223 decay3Body.getProngID(1),
1224 decay3Body.getProngID(2)};
1225 int tableIdx[3]{-1, -1, -1}, collID = -1;
1226 bool missing{false};
1227 for (int i{0}; i < 3; ++i) {
1228 auto item = mGIDToTableID.find(trIDs[i]);
1229 if (item != mGIDToTableID.end()) {
1230 tableIdx[i] = item->second;
1231 } else {
1232 LOG(warn) << fmt::format("Could not find a track index for prong ID {}", (int)trIDs[i]);
1233 missing = true;
1234 }
1235 }
1236 auto itemV = mVtxToTableCollID.find(decay3Body.getVertexID());
1237 if (itemV == mVtxToTableCollID.end()) {
1238 LOG(warn) << "Could not find 3 body collisionID for the vertex ID " << decay3Body.getVertexID();
1239 missing = true;
1240 } else {
1241 collID = itemV->second;
1242 }
1243 if (missing) {
1244 continue;
1245 }
1246 decay3BodyCursor(collID, tableIdx[0], tableIdx[1], tableIdx[2]);
1247 }
1248}
1249
1250template <typename FwdTrkClsCursorType>
1251void AODProducerWorkflowDPL::addClustersToFwdTrkClsTable(const o2::globaltracking::RecoContainer& recoData, FwdTrkClsCursorType& fwdTrkClsCursor, GIndex trackID, int fwdTrackId)
1252{
1253 const auto& mchTracks = recoData.getMCHTracks();
1254 const auto& mchmidMatches = recoData.getMCHMIDMatches();
1255 const auto& mchClusters = recoData.getMCHTrackClusters();
1256
1257 int mchTrackID = -1;
1258 if (trackID.getSource() == GIndex::MCH) { // This is an MCH track
1259 mchTrackID = trackID.getIndex();
1260 } else if (trackID.getSource() == GIndex::MCHMID) { // This is an MCH-MID track
1261 auto mchmidMatch = mchmidMatches[trackID.getIndex()];
1262 mchTrackID = mchmidMatch.getMCHRef().getIndex();
1263 } // Others are Global Forward Tracks, their clusters will be or were added with the corresponding MCH track
1264
1265 if (mchTrackID > -1 && mchTrackID < mchTracks.size()) {
1266 const auto& mchTrack = mchTracks[mchTrackID];
1267 fwdTrkClsCursor.reserve(mchTrack.getNClusters() + fwdTrkClsCursor.lastIndex());
1268 int first = mchTrack.getFirstClusterIdx();
1269 int last = mchTrack.getLastClusterIdx();
1270 for (int i = first; i <= last; i++) {
1271 const auto& cluster = mchClusters[i];
1272 fwdTrkClsCursor(fwdTrackId,
1273 truncateFloatFraction(cluster.x, mMuonCl),
1274 truncateFloatFraction(cluster.y, mMuonCl),
1275 truncateFloatFraction(cluster.z, mMuonCl),
1276 (((cluster.ey < 5.) & 0x1) << 12) | (((cluster.ex < 5.) & 0x1) << 11) | cluster.getDEId());
1277 }
1278 }
1279}
1280
1281template <typename HMPCursorType>
1282void AODProducerWorkflowDPL::fillHMPID(const o2::globaltracking::RecoContainer& recoData, HMPCursorType& hmpCursor)
1283{
1284 auto hmpMatches = recoData.getHMPMatches();
1285
1286 hmpCursor.reserve(hmpMatches.size());
1287
1288 // filling HMPs table
1289 for (size_t iHmp = 0; iHmp < hmpMatches.size(); iHmp++) {
1290
1291 const auto& match = hmpMatches[iHmp];
1292
1293 float xTrk, yTrk, theta, phi;
1294 float xMip, yMip;
1295 int charge, nph;
1296
1297 match.getHMPIDtrk(xTrk, yTrk, theta, phi);
1298 match.getHMPIDmip(xMip, yMip, charge, nph);
1299
1300 auto photChargeVec = match.getPhotCharge();
1301
1302 float photChargeVec2[10]; // = {0.,0.,0.,0.,0.,0.,0.,0.,0.,0.};
1303
1304 for (Int_t i = 0; i < 10; i++) {
1305 photChargeVec2[i] = photChargeVec[i];
1306 }
1307 auto tref = mGIDToTableID.find(match.getTrackRef());
1308 if (tref != mGIDToTableID.end()) {
1309 hmpCursor(tref->second, match.getHMPsignal(), xTrk, yTrk, xMip, yMip, nph, charge, match.getIdxHMPClus(), match.getHmpMom(), photChargeVec2);
1310 } else {
1311 LOG(error) << "Could not find AOD track table entry for HMP-matched track " << match.getTrackRef().asString();
1312 }
1313 }
1314}
1315
1316void AODProducerWorkflowDPL::prepareStrangenessTracking(const o2::globaltracking::RecoContainer& recoData)
1317{
1318 auto v0s = recoData.getV0sIdx();
1319 auto cascades = recoData.getCascadesIdx();
1320 auto decays3Body = recoData.getDecays3BodyIdx();
1321
1322 int sTrkID = 0;
1323 mCollisionStrTrk.clear();
1324 mCollisionStrTrk.reserve(recoData.getStrangeTracks().size());
1325 mVertexStrLUT.clear();
1326 mVertexStrLUT.resize(recoData.getPrimaryVertices().size() + 1, 0);
1327 for (auto& sTrk : recoData.getStrangeTracks()) {
1328 auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS};
1329 int vtxId{0};
1330 if (sTrk.mPartType == dataformats::kStrkV0) {
1331 vtxId = v0s[sTrk.mDecayRef].getVertexID();
1332 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1333 vtxId = cascades[sTrk.mDecayRef].getVertexID();
1334 } else {
1335 vtxId = decays3Body[sTrk.mDecayRef].getVertexID();
1336 }
1337 mCollisionStrTrk.emplace_back(vtxId, sTrkID++);
1338 mVertexStrLUT[vtxId]++;
1339 }
1340 std::exclusive_scan(mVertexStrLUT.begin(), mVertexStrLUT.end(), mVertexStrLUT.begin(), 0);
1341
1342 // sort by collision ID
1343 std::sort(mCollisionStrTrk.begin(), mCollisionStrTrk.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
1344 mStrTrkIndices.clear();
1345 mStrTrkIndices.resize(mCollisionStrTrk.size(), -1);
1346}
1347
1348template <typename V0C, typename CC, typename D3BC>
1349void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltracking::RecoContainer& recoData, V0C& v0Curs, CC& cascCurs, D3BC& d3BodyCurs)
1350{
1351 int itsTableIdx = -1;
1352 int sTrkID = 0;
1353 int nV0 = 0;
1354 int nCasc = 0;
1355 int nD3Body = 0;
1356
1357 for (const auto& sTrk : recoData.getStrangeTracks()) {
1358 if (sTrk.mPartType == dataformats::kStrkV0) {
1359 nV0++;
1360 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1361 nCasc++;
1362 } else {
1363 nD3Body++;
1364 }
1365 }
1366
1367 v0Curs.reserve(nV0);
1368 cascCurs.reserve(nCasc);
1369 d3BodyCurs.reserve(nD3Body);
1370
1371 for (const auto& sTrk : recoData.getStrangeTracks()) {
1372 auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS};
1373 auto item = mGIDToTableID.find(ITSIndex);
1374 if (item != mGIDToTableID.end()) {
1375 itsTableIdx = item->second;
1376 } else {
1377 LOG(warn) << "Could not find a ITS strange track index " << ITSIndex;
1378 continue;
1379 }
1380 if (sTrk.mPartType == dataformats::kStrkV0) {
1381 v0Curs(mStrTrkIndices[sTrkID++],
1382 itsTableIdx,
1383 sTrk.mDecayRef,
1384 sTrk.mDecayVtx[0],
1385 sTrk.mDecayVtx[1],
1386 sTrk.mDecayVtx[2],
1387 sTrk.mMasses[0],
1388 sTrk.mMasses[1],
1389 sTrk.mMatchChi2,
1390 sTrk.mTopoChi2,
1391 sTrk.getAverageClusterSize());
1392 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1393 cascCurs(mStrTrkIndices[sTrkID++],
1394 itsTableIdx,
1395 sTrk.mDecayRef,
1396 sTrk.mDecayVtx[0],
1397 sTrk.mDecayVtx[1],
1398 sTrk.mDecayVtx[2],
1399 sTrk.mMasses[0],
1400 sTrk.mMasses[1],
1401 sTrk.mMatchChi2,
1402 sTrk.mTopoChi2,
1403 sTrk.getAverageClusterSize());
1404 } else {
1405 d3BodyCurs(mStrTrkIndices[sTrkID++],
1406 itsTableIdx,
1407 sTrk.mDecayRef,
1408 sTrk.mDecayVtx[0],
1409 sTrk.mDecayVtx[1],
1410 sTrk.mDecayVtx[2],
1411 sTrk.mMasses[0],
1412 sTrk.mMasses[1],
1413 sTrk.mMatchChi2,
1414 sTrk.mTopoChi2,
1415 sTrk.getAverageClusterSize());
1416 }
1417 }
1418}
1419
1420void AODProducerWorkflowDPL::countTPCClusters(const o2::globaltracking::RecoContainer& data)
1421{
1422 const auto& tpcTracks = data.getTPCTracks();
1423 const auto& tpcClusRefs = data.getTPCTracksClusterRefs();
1424 const auto& tpcClusShMap = data.clusterShMapTPC;
1425 const auto& tpcClusAcc = data.getTPCClusters();
1426 constexpr int maxRows = 152;
1427 constexpr int neighbour = 2;
1428 int ntr = tpcTracks.size();
1429 mTPCCounters.clear();
1430 mTPCCounters.resize(ntr);
1431#ifdef WITH_OPENMP
1432 int ngroup = std::min(50, std::max(1, ntr / mNThreads));
1433#pragma omp parallel for schedule(dynamic, ngroup) num_threads(mNThreads)
1434#endif
1435 for (int itr = 0; itr < ntr; itr++) {
1436 std::array<bool, maxRows> clMap{}, shMap{};
1437 uint8_t sectorIndex, rowIndex;
1438 uint32_t clusterIndex;
1439 auto& counters = mTPCCounters[itr];
1440 const auto& track = tpcTracks[itr];
1441 for (int i = 0; i < track.getNClusterReferences(); i++) {
1442 o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef());
1443 unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex;
1444 clMap[rowIndex] = true;
1445 if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) {
1446 if (!shMap[rowIndex]) {
1447 counters.shared++;
1448 }
1449 shMap[rowIndex] = true;
1450 }
1451 }
1452 int last = -1;
1453 for (int i = 0; i < maxRows; i++) {
1454 if (clMap[i]) {
1455 counters.crossed++;
1456 counters.found++;
1457 last = i;
1458 } else if ((i - last) <= neighbour) {
1459 counters.crossed++;
1460 } else {
1461 int lim = std::min(i + 1 + neighbour, maxRows);
1462 for (int j = i + 1; j < lim; j++) {
1463 if (clMap[j]) {
1464 counters.crossed++;
1465 break;
1466 }
1467 }
1468 }
1469 }
1470 }
1471}
1472
1473uint8_t AODProducerWorkflowDPL::getTRDPattern(const o2::trd::TrackTRD& track)
1474{
1475 uint8_t pattern = 0;
1476 for (int il = o2::trd::TrackTRD::EGPUTRDTrack::kNLayers - 1; il >= 0; il--) {
1477 if (track.getTrackletIndex(il) != -1) {
1478 pattern |= 0x1 << il;
1479 }
1480 }
1481 if (track.getHasNeighbor()) {
1482 pattern |= 0x1 << 6;
1483 }
1484 if (track.getHasPadrowCrossing()) {
1485 pattern |= 0x1 << 7;
1486 }
1487 return pattern;
1488}
1489
1490template <typename TCaloHandler, typename TCaloCursor, typename TCaloTRGCursor, typename TMCCaloLabelCursor>
1491void AODProducerWorkflowDPL::addToCaloTable(TCaloHandler& caloHandler, TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor,
1492 TMCCaloLabelCursor& mcCaloCellLabelCursor, int eventID, int bcID, int8_t caloType)
1493{
1494 auto inputEvent = caloHandler.buildEvent(eventID);
1495 auto cellsInEvent = inputEvent.mCells; // get cells belonging to current event
1496 auto cellMClabels = inputEvent.mMCCellLabels; // get MC labels belonging to current event (only implemented for EMCal currently!)
1497 caloCellCursor.reserve(cellsInEvent.size() + caloCellCursor.lastIndex());
1498 caloTRGCursor.reserve(cellsInEvent.size() + caloTRGCursor.lastIndex());
1499 if (mUseMC) {
1500 mcCaloCellLabelCursor.reserve(cellsInEvent.size() + mcCaloCellLabelCursor.lastIndex());
1501 }
1502 for (auto iCell = 0U; iCell < cellsInEvent.size(); iCell++) {
1503 caloCellCursor(bcID,
1504 CellHelper::getCellNumber(cellsInEvent[iCell]),
1505 truncateFloatFraction(CellHelper::getAmplitude(cellsInEvent[iCell]), mCaloAmp),
1506 truncateFloatFraction(CellHelper::getTimeStamp(cellsInEvent[iCell]), mCaloTime),
1507 cellsInEvent[iCell].getType(),
1508 caloType); // 1 = emcal, -1 = undefined, 0 = phos
1509
1510 // todo: fix dummy values in CellHelper when it is clear what is filled for trigger information
1511 if (CellHelper::isTRU(cellsInEvent[iCell])) { // Write only trigger cells into this table
1512 caloTRGCursor(bcID,
1513 CellHelper::getFastOrAbsID(cellsInEvent[iCell]),
1514 CellHelper::getLnAmplitude(cellsInEvent[iCell]),
1515 CellHelper::getTriggerBits(cellsInEvent[iCell]),
1516 caloType);
1517 }
1518 if (mUseMC) {
1519 // Common for PHOS and EMCAL
1520 // loop over all MC Labels for the current cell
1521 std::vector<int32_t> particleIds;
1522 std::vector<float> amplitudeFraction;
1523 if (!mEMCselectLeading) {
1524 particleIds.reserve(cellMClabels.size());
1525 amplitudeFraction.reserve(cellMClabels.size());
1526 }
1527 float tmpMaxAmplitude = 0;
1528 int32_t tmpindex = 0;
1529 for (auto& mclabel : cellMClabels[iCell]) {
1530 // do not fill noise lables!
1531 if (mclabel.isValid()) {
1532 if (mEMCselectLeading) {
1533 if (mclabel.getAmplitudeFraction() > tmpMaxAmplitude) {
1534 // Check if this MCparticle added to be kept?
1535 if (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).find(mclabel.getTrackID()) !=
1536 mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).end()) {
1537 tmpMaxAmplitude = mclabel.getAmplitudeFraction();
1538 tmpindex = (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID())).at(mclabel.getTrackID());
1539 }
1540 }
1541 } else {
1542 auto trackStore = mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID());
1543 auto iter = trackStore.find(mclabel.getTrackID());
1544 if (iter != trackStore.end()) {
1545 amplitudeFraction.emplace_back(mclabel.getAmplitudeFraction());
1546 particleIds.emplace_back(iter->second);
1547 } else {
1548 particleIds.emplace_back(-1); // should the mc particle not be in mToStore make sure something (e.g. -1) is saved in particleIds so the length of particleIds is the same es amplitudeFraction!
1549 amplitudeFraction.emplace_back(0.f);
1550 LOG(warn) << "CaloTable: Could not find track for mclabel (" << mclabel.getSourceID() << "," << mclabel.getEventID() << "," << mclabel.getTrackID() << ") in the AOD MC store";
1551 if (mMCKineReader) {
1552 auto mctrack = mMCKineReader->getTrack(mclabel);
1553 TVector3 vec;
1554 mctrack->GetStartVertex(vec);
1555 LOG(warn) << " ... this track is of PDG " << mctrack->GetPdgCode() << " produced by " << mctrack->getProdProcessAsString() << " at (" << vec.X() << "," << vec.Y() << "," << vec.Z() << ")";
1556 }
1557 }
1558 }
1559 }
1560 } // end of loop over all MC Labels for the current cell
1561 if (mEMCselectLeading) {
1562 amplitudeFraction.emplace_back(tmpMaxAmplitude);
1563 particleIds.emplace_back(tmpindex);
1564 }
1565 if (particleIds.size() == 0) {
1566 particleIds.emplace_back(-1);
1567 amplitudeFraction.emplace_back(0.f);
1568 }
1569 mcCaloCellLabelCursor(particleIds,
1570 amplitudeFraction);
1571 }
1572 } // end of loop over cells in current event
1573}
1574
1575// fill calo related tables (cells and calotrigger table)
1576template <typename TCaloCursor, typename TCaloTRGCursor, typename TMCCaloLabelCursor>
1577void AODProducerWorkflowDPL::fillCaloTable(TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor,
1578 TMCCaloLabelCursor& mcCaloCellLabelCursor, const std::map<uint64_t, int>& bcsMap,
1580{
1581 // get calo information
1582 auto caloEMCCells = data.getEMCALCells();
1583 auto caloEMCCellsTRGR = data.getEMCALTriggers();
1584 auto mcCaloEMCCellLabels = data.getEMCALCellsMCLabels();
1585
1586 auto caloPHOSCells = data.getPHOSCells();
1587 auto caloPHOSCellsTRGR = data.getPHOSTriggers();
1588 auto mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels();
1589
1590 if (!mInputSources[GIndex::PHS]) {
1591 caloPHOSCells = {};
1592 caloPHOSCellsTRGR = {};
1593 mcCaloPHOSCellLabels = {};
1594 }
1595
1596 if (!mInputSources[GIndex::EMC]) {
1597 caloEMCCells = {};
1598 caloEMCCellsTRGR = {};
1599 mcCaloEMCCellLabels = {};
1600 }
1601
1604
1605 // get cell belonging to an eveffillnt instead of timeframe
1606 emcEventHandler.reset();
1607 emcEventHandler.setCellData(caloEMCCells, caloEMCCellsTRGR);
1608 emcEventHandler.setCellMCTruthContainer(mcCaloEMCCellLabels);
1609
1610 phsEventHandler.reset();
1611 phsEventHandler.setCellData(caloPHOSCells, caloPHOSCellsTRGR);
1612 phsEventHandler.setCellMCTruthContainer(mcCaloPHOSCellLabels);
1613
1614 int emcNEvents = emcEventHandler.getNumberOfEvents();
1615 int phsNEvents = phsEventHandler.getNumberOfEvents();
1616
1617 std::vector<std::tuple<uint64_t, int8_t, int>> caloEvents; // <bc, caloType, eventID>
1618
1619 caloEvents.reserve(emcNEvents + phsNEvents);
1620
1621 for (int iev = 0; iev < emcNEvents; ++iev) {
1622 uint64_t bc = emcEventHandler.getInteractionRecordForEvent(iev).toLong();
1623 caloEvents.emplace_back(std::make_tuple(bc, 1, iev));
1624 }
1625
1626 for (int iev = 0; iev < phsNEvents; ++iev) {
1627 uint64_t bc = phsEventHandler.getInteractionRecordForEvent(iev).toLong();
1628 caloEvents.emplace_back(std::make_tuple(bc, 0, iev));
1629 }
1630
1631 std::sort(caloEvents.begin(), caloEvents.end(),
1632 [](const auto& left, const auto& right) { return std::get<0>(left) < std::get<0>(right); });
1633
1634 // loop over events
1635 for (int i = 0; i < emcNEvents + phsNEvents; ++i) {
1636 uint64_t globalBC = std::get<0>(caloEvents[i]);
1637 int8_t caloType = std::get<1>(caloEvents[i]);
1638 int eventID = std::get<2>(caloEvents[i]);
1639 auto item = bcsMap.find(globalBC);
1640 int bcID = -1;
1641 if (item != bcsMap.end()) {
1642 bcID = item->second;
1643 } else {
1644 LOG(warn) << "Error: could not find a corresponding BC ID for a calo point; globalBC = " << globalBC << ", caloType = " << (int)caloType;
1645 }
1646 if (caloType == 0) { // phos
1647 addToCaloTable(phsEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType);
1648 }
1649 if (caloType == 1) { // emc
1650 addToCaloTable(emcEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType);
1651 }
1652 }
1653
1654 caloEvents.clear();
1655}
1656
1658{
1659 mTimer.Stop();
1661 mLPMProdTag = ic.options().get<string>("lpmp-prod-tag");
1662 mAnchorPass = ic.options().get<string>("anchor-pass");
1663 mAnchorProd = ic.options().get<string>("anchor-prod");
1664 mRecoPass = ic.options().get<string>("reco-pass");
1665 mTFNumber = ic.options().get<int64_t>("aod-timeframe-id");
1666 mRecoOnly = ic.options().get<int>("reco-mctracks-only");
1667 mTruncate = ic.options().get<int>("enable-truncation");
1668 mRunNumber = ic.options().get<int>("run-number");
1669 mCTPReadout = ic.options().get<int>("ctpreadout-create");
1670 mNThreads = std::max(1, ic.options().get<int>("nthreads"));
1671 mEMCselectLeading = ic.options().get<bool>("emc-select-leading");
1672 mThinTracks = ic.options().get<bool>("thin-tracks");
1673 mPropTracks = ic.options().get<bool>("propagate-tracks");
1674 mPropMuons = ic.options().get<bool>("propagate-muons");
1675 if (auto s = ic.options().get<std::string>("with-streamers"); !s.empty()) {
1676 mStreamerFlags.set(s);
1677 if (mStreamerFlags) {
1678 LOGP(info, "Writing streamer data with mask:");
1679 LOG(info) << mStreamerFlags;
1680 } else {
1681 LOGP(warn, "Specified non-default empty streamer mask!");
1682 }
1683 }
1684 mTrackQCFraction = ic.options().get<float>("trackqc-fraction");
1685 mTrackQCNTrCut = ic.options().get<int64_t>("trackqc-NTrCut");
1686 if (auto seed = ic.options().get<int>("seed"); seed == 0) {
1687 LOGP(info, "Using random device for seeding");
1688 std::random_device rd;
1689 std::array<int, std::mt19937::state_size> seed_data{};
1690 std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
1691 std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
1692 mGenerator = std::mt19937(seq);
1693 } else {
1694 LOGP(info, "Using seed {} for sampling", seed);
1695 mGenerator.seed(seed);
1696 }
1697#ifdef WITH_OPENMP
1698 LOGP(info, "Multi-threaded parts will run with {} OpenMP threads", mNThreads);
1699#else
1700 mNThreads = 1;
1701 LOG(info) << "OpenMP is disabled";
1702#endif
1703 if (mTFNumber == -1L) {
1704 LOG(info) << "TFNumber will be obtained from CCDB";
1705 }
1706 if (mRunNumber == -1L) {
1707 LOG(info) << "The Run number will be obtained from DPL headers";
1708 }
1709
1710 // set no truncation if selected by user
1711 if (mTruncate != 1) {
1712 LOG(info) << "Truncation is not used!";
1713 mCollisionPosition = 0xFFFFFFFF;
1714 mCollisionPositionCov = 0xFFFFFFFF;
1715 mTrackX = 0xFFFFFFFF;
1716 mTrackAlpha = 0xFFFFFFFF;
1717 mTrackSnp = 0xFFFFFFFF;
1718 mTrackTgl = 0xFFFFFFFF;
1719 mTrack1Pt = 0xFFFFFFFF;
1720 mTrackChi2 = 0xFFFFFFFF;
1721 mTrackCovDiag = 0xFFFFFFFF;
1722 mTrackCovOffDiag = 0xFFFFFFFF;
1723 mTrackSignal = 0xFFFFFFFF;
1724 mTrackTime = 0xFFFFFFFF;
1725 mTPCTime0 = 0xFFFFFFFF;
1726 mTrackTimeError = 0xFFFFFFFF;
1727 mTrackPosEMCAL = 0xFFFFFFFF;
1728 mTracklets = 0xFFFFFFFF;
1729 mMcParticleW = 0xFFFFFFFF;
1730 mMcParticlePos = 0xFFFFFFFF;
1731 mMcParticleMom = 0xFFFFFFFF;
1732 mCaloAmp = 0xFFFFFFFF;
1733 mCaloTime = 0xFFFFFFFF;
1734 mCPVPos = 0xFFFFFFFF;
1735 mCPVAmpl = 0xFFFFFFFF;
1736 mMuonTr1P = 0xFFFFFFFF;
1737 mMuonTrThetaX = 0xFFFFFFFF;
1738 mMuonTrThetaY = 0xFFFFFFFF;
1739 mMuonTrZmu = 0xFFFFFFFF;
1740 mMuonTrBend = 0xFFFFFFFF;
1741 mMuonTrNonBend = 0xFFFFFFFF;
1742 mMuonTrCov = 0xFFFFFFFF;
1743 mMuonCl = 0xFFFFFFFF;
1744 mMuonClErr = 0xFFFFFFFF;
1745 mV0Time = 0xFFFFFFFF;
1746 mV0ChannelTime = 0xFFFFFFFF;
1747 mFDDTime = 0xFFFFFFFF;
1748 mFDDChannelTime = 0xFFFFFFFF;
1749 mT0Time = 0xFFFFFFFF;
1750 mT0ChannelTime = 0xFFFFFFFF;
1751 mV0Amplitude = 0xFFFFFFFF;
1752 mFDDAmplitude = 0xFFFFFFFF;
1753 mT0Amplitude = 0xFFFFFFFF;
1754 }
1755 // Initialize ZDC helper maps
1756 for (int ic = 0; ic < o2::zdc::NChannels; ic++) {
1757 mZDCEnergyMap[ic] = -std::numeric_limits<float>::infinity();
1758 }
1759 for (int ic = 0; ic < o2::zdc::NTDCChannels; ic++) {
1760 mZDCTDCMap[ic] = -std::numeric_limits<float>::infinity();
1761 }
1762
1763 std::string hepmcUpdate = ic.options().get<std::string>("hepmc-update");
1764 HepMCUpdate when = (hepmcUpdate == "never" ? HepMCUpdate::never : hepmcUpdate == "always" ? HepMCUpdate::always
1765 : hepmcUpdate == "all" ? HepMCUpdate::allKeys
1766 : HepMCUpdate::anyKey);
1767 mXSectionUpdate = when;
1768 mPdfInfoUpdate = when;
1769 mHeavyIonUpdate = when;
1770
1771 mTimer.Reset();
1772
1773 if (mStreamerFlags) {
1774 mStreamer = std::make_unique<o2::utils::TreeStreamRedirector>("AO2DStreamer.root", "RECREATE");
1775 }
1776}
1777
1779{
1780 mTimer.Start(false);
1782 recoData.collectData(pc, *mDataRequest);
1783 updateTimeDependentParams(pc); // Make sure that this is called after the RecoContainer collect data, since some condition objects are fetched there
1784
1785 mStartIR = recoData.startIR;
1786
1787 auto primVertices = recoData.getPrimaryVertices();
1788 auto primVer2TRefs = recoData.getPrimaryVertexMatchedTrackRefs();
1789 auto primVerGIs = recoData.getPrimaryVertexMatchedTracks();
1790 auto primVerLabels = recoData.getPrimaryVertexMCLabels();
1791
1792 auto fddChData = recoData.getFDDChannelsData();
1793 auto fddRecPoints = recoData.getFDDRecPoints();
1794 auto ft0ChData = recoData.getFT0ChannelsData();
1795 auto ft0RecPoints = recoData.getFT0RecPoints();
1796 auto fv0ChData = recoData.getFV0ChannelsData();
1797 auto fv0RecPoints = recoData.getFV0RecPoints();
1798
1799 auto zdcEnergies = recoData.getZDCEnergy();
1800 auto zdcBCRecData = recoData.getZDCBCRecData();
1801 auto zdcTDCData = recoData.getZDCTDCData();
1802
1803 auto cpvClusters = recoData.getCPVClusters();
1804 auto cpvTrigRecs = recoData.getCPVTriggers();
1805
1806 auto ctpDigits = recoData.getCTPDigits();
1807 const auto& tinfo = pc.services().get<o2::framework::TimingInfo>();
1808 std::vector<o2::ctp::CTPDigit> ctpDigitsCreated;
1809 if (mCTPReadout == 1) {
1810 LOG(info) << "CTP : creating ctpreadout in AOD producer";
1811 createCTPReadout(recoData, ctpDigitsCreated, pc);
1812 LOG(info) << "CTP : ctpreadout created from AOD";
1813 ctpDigits = gsl::span<o2::ctp::CTPDigit>(ctpDigitsCreated);
1814 }
1815 LOG(debug) << "FOUND " << primVertices.size() << " primary vertices";
1816 LOG(debug) << "FOUND " << ft0RecPoints.size() << " FT0 rec. points";
1817 LOG(debug) << "FOUND " << fv0RecPoints.size() << " FV0 rec. points";
1818 LOG(debug) << "FOUND " << fddRecPoints.size() << " FDD rec. points";
1819 LOG(debug) << "FOUND " << cpvClusters.size() << " CPV clusters";
1820 LOG(debug) << "FOUND " << cpvTrigRecs.size() << " CPV trigger records";
1821
1822 LOG(info) << "FOUND " << primVertices.size() << " primary vertices";
1823
1824 using namespace o2::aodhelpers;
1825
1826 auto bcCursor = createTableCursor<o2::aod::BCs>(pc);
1827 auto bcFlagsCursor = createTableCursor<o2::aod::BCFlags>(pc);
1828 auto cascadesCursor = createTableCursor<o2::aod::Cascades>(pc);
1829 auto collisionsCursor = createTableCursor<o2::aod::Collisions>(pc);
1830 auto decay3BodyCursor = createTableCursor<o2::aod::Decay3Bodys>(pc);
1831 auto trackedCascadeCursor = createTableCursor<o2::aod::TrackedCascades>(pc);
1832 auto trackedV0Cursor = createTableCursor<o2::aod::TrackedV0s>(pc);
1833 auto tracked3BodyCurs = createTableCursor<o2::aod::Tracked3Bodys>(pc);
1834 auto fddCursor = createTableCursor<o2::aod::FDDs>(pc);
1835 auto fddExtraCursor = createTableCursor<o2::aod::FDDsExtra>(pc);
1836 auto ft0Cursor = createTableCursor<o2::aod::FT0s>(pc);
1837 auto ft0ExtraCursor = createTableCursor<o2::aod::FT0sExtra>(pc);
1838 auto fv0aCursor = createTableCursor<o2::aod::FV0As>(pc);
1839 auto fv0aExtraCursor = createTableCursor<o2::aod::FV0AsExtra>(pc);
1840 auto fwdTracksCursor = createTableCursor<o2::aod::StoredFwdTracks>(pc);
1841 auto fwdTracksCovCursor = createTableCursor<o2::aod::StoredFwdTracksCov>(pc);
1842 auto fwdTrkClsCursor = createTableCursor<o2::aod::FwdTrkCls>(pc);
1843 auto mftTracksCursor = createTableCursor<o2::aod::StoredMFTTracks>(pc);
1844 auto mftTracksCovCursor = createTableCursor<o2::aod::StoredMFTTracksCov>(pc);
1845 auto tracksCursor = createTableCursor<o2::aod::StoredTracksIU>(pc);
1846 auto tracksCovCursor = createTableCursor<o2::aod::StoredTracksCovIU>(pc);
1847 auto tracksExtraCursor = createTableCursor<o2::aod::StoredTracksExtra>(pc);
1848 auto tracksQACursor = createTableCursor<o2::aod::TracksQAVersion>(pc);
1849 auto ambigTracksCursor = createTableCursor<o2::aod::AmbiguousTracks>(pc);
1850 auto ambigMFTTracksCursor = createTableCursor<o2::aod::AmbiguousMFTTracks>(pc);
1851 auto ambigFwdTracksCursor = createTableCursor<o2::aod::AmbiguousFwdTracks>(pc);
1852 auto v0sCursor = createTableCursor<o2::aod::V0s>(pc);
1853 auto zdcCursor = createTableCursor<o2::aod::Zdcs>(pc);
1854 auto hmpCursor = createTableCursor<o2::aod::HMPIDs>(pc);
1855 auto caloCellsCursor = createTableCursor<o2::aod::Calos>(pc);
1856 auto caloCellsTRGTableCursor = createTableCursor<o2::aod::CaloTriggers>(pc);
1857 auto cpvClustersCursor = createTableCursor<o2::aod::CPVClusters>(pc);
1858 auto originCursor = createTableCursor<o2::aod::Origins>(pc);
1859
1860 // Declare MC cursors type without adding the output for a table
1871 if (mUseMC) { // This creates the actual writercursor
1872 mcColLabelsCursor = createTableCursor<o2::aod::McCollisionLabels>(pc);
1873 mcCollisionsCursor = createTableCursor<o2::aod::McCollisions>(pc);
1874 hepmcXSectionsCursor = createTableCursor<o2::aod::HepMCXSections>(pc);
1875 hepmcPdfInfosCursor = createTableCursor<o2::aod::HepMCPdfInfos>(pc);
1876 hepmcHeavyIonsCursor = createTableCursor<o2::aod::HepMCHeavyIons>(pc);
1877 mcMFTTrackLabelCursor = createTableCursor<o2::aod::McMFTTrackLabels>(pc);
1878 mcFwdTrackLabelCursor = createTableCursor<o2::aod::McFwdTrackLabels>(pc);
1879 mcParticlesCursor = createTableCursor<o2::aod::StoredMcParticles_001>(pc);
1880 mcTrackLabelCursor = createTableCursor<o2::aod::McTrackLabels>(pc);
1881 mcCaloLabelsCursor = createTableCursor<o2::aod::McCaloLabels_001>(pc);
1882 }
1883
1884 std::unique_ptr<o2::steer::MCKinematicsReader> mcReader;
1885 if (mUseMC) {
1886 mcReader = std::make_unique<o2::steer::MCKinematicsReader>("collisioncontext.root");
1887 }
1888 mMCKineReader = mcReader.get(); // for use in different functions
1889 std::map<uint64_t, int> bcsMap;
1890 collectBCs(recoData, mUseMC ? mcReader->getDigitizationContext()->getEventRecords() : std::vector<o2::InteractionTimeRecord>{}, bcsMap);
1891 if (!primVer2TRefs.empty()) { // if the vertexing was done, the last slot refers to orphan tracks
1892 addRefGlobalBCsForTOF(primVer2TRefs.back(), primVerGIs, recoData, bcsMap);
1893 }
1894 // initialize the bunch crossing container for further use below
1895 mBCLookup.init(bcsMap);
1896
1897 uint64_t tfNumber;
1898 const int runNumber = (mRunNumber == -1) ? int(tinfo.runNumber) : mRunNumber;
1899 if (mTFNumber == -1L) {
1900 // TODO has to use absolute time of TF
1901 tfNumber = uint64_t(tinfo.firstTForbit) + (uint64_t(tinfo.runNumber) << 32); // getTFNumber(mStartIR, runNumber);
1902 } else {
1903 tfNumber = mTFNumber;
1904 }
1905
1906 std::vector<float> aAmplitudes, aTimes;
1907 std::vector<uint8_t> aChannels;
1908 fv0aCursor.reserve(fv0RecPoints.size());
1909 for (auto& fv0RecPoint : fv0RecPoints) {
1910 aAmplitudes.clear();
1911 aChannels.clear();
1912 aTimes.clear();
1913 const auto channelData = fv0RecPoint.getBunchChannelData(fv0ChData);
1914 for (auto& channel : channelData) {
1915 if (channel.charge > 0) {
1916 aAmplitudes.push_back(truncateFloatFraction(channel.charge, mV0Amplitude));
1917 aTimes.push_back(truncateFloatFraction(channel.time * 1.E-3, mV0ChannelTime));
1918 aChannels.push_back(channel.channel);
1919 }
1920 }
1921 uint64_t bc = fv0RecPoint.getInteractionRecord().toLong();
1922 auto item = bcsMap.find(bc);
1923 int bcID = -1;
1924 if (item != bcsMap.end()) {
1925 bcID = item->second;
1926 } else {
1927 LOG(fatal) << "Error: could not find a corresponding BC ID for a FV0 rec. point; BC = " << bc;
1928 }
1929 fv0aCursor(bcID,
1930 aAmplitudes,
1931 aChannels,
1932 truncateFloatFraction(fv0RecPoint.getCollisionGlobalMeanTime() * 1E-3, mV0Time), // ps to ns
1933 fv0RecPoint.getTrigger().getTriggersignals());
1934
1935 if (mEnableFITextra) {
1936 fv0aExtraCursor(bcID,
1937 aTimes);
1938 }
1939 }
1940
1941 std::vector<float> zdcEnergy, zdcAmplitudes, zdcTime;
1942 std::vector<uint8_t> zdcChannelsE, zdcChannelsT;
1943 zdcCursor.reserve(zdcBCRecData.size());
1944 for (auto zdcRecData : zdcBCRecData) {
1945 uint64_t bc = zdcRecData.ir.toLong();
1946 auto item = bcsMap.find(bc);
1947 int bcID = -1;
1948 if (item != bcsMap.end()) {
1949 bcID = item->second;
1950 } else {
1951 LOG(fatal) << "Error: could not find a corresponding BC ID for a ZDC rec. point; BC = " << bc;
1952 }
1953 int fe, ne, ft, nt, fi, ni;
1954 zdcRecData.getRef(fe, ne, ft, nt, fi, ni);
1955 zdcEnergy.clear();
1956 zdcChannelsE.clear();
1957 zdcAmplitudes.clear();
1958 zdcTime.clear();
1959 zdcChannelsT.clear();
1960 for (int ie = 0; ie < ne; ie++) {
1961 auto& zdcEnergyData = zdcEnergies[fe + ie];
1962 zdcEnergy.emplace_back(zdcEnergyData.energy());
1963 zdcChannelsE.emplace_back(zdcEnergyData.ch());
1964 }
1965 for (int it = 0; it < nt; it++) {
1966 auto& tdc = zdcTDCData[ft + it];
1967 zdcAmplitudes.emplace_back(tdc.amplitude());
1968 zdcTime.emplace_back(tdc.value());
1969 zdcChannelsT.emplace_back(o2::zdc::TDCSignal[tdc.ch()]);
1970 }
1971 zdcCursor(bcID,
1972 zdcEnergy,
1973 zdcChannelsE,
1974 zdcAmplitudes,
1975 zdcTime,
1976 zdcChannelsT);
1977 }
1978
1979 // keep track event/source id for each mc-collision
1980 // using map and not unordered_map to ensure
1981 // correct ordering when iterating over container elements
1982 std::vector<std::vector<int>> mcColToEvSrc;
1983
1984 if (mUseMC) {
1985 using namespace o2::aodmchelpers;
1986
1987 // filling mcCollision table
1988 int nMCCollisions = mcReader->getDigitizationContext()->getNCollisions();
1989 const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords();
1990 const auto& mcParts = mcReader->getDigitizationContext()->getEventParts();
1991
1992 // count all parts
1993 int totalNParts = 0;
1994 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
1995 totalNParts += mcParts[iCol].size();
1996 }
1997 mcCollisionsCursor.reserve(totalNParts);
1998
1999 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
2000 const auto time = mcRecords[iCol].getTimeOffsetWrtBC();
2001 auto globalBC = mcRecords[iCol].toLong();
2002 auto item = bcsMap.find(globalBC);
2003 int bcID = -1;
2004 if (item != bcsMap.end()) {
2005 bcID = item->second;
2006 } else {
2007 LOG(fatal) << "Error: could not find a corresponding BC ID "
2008 << "for MC collision; BC = " << globalBC
2009 << ", mc collision = " << iCol;
2010 }
2011 auto& colParts = mcParts[iCol];
2012 auto nParts = colParts.size();
2013 for (auto colPart : colParts) {
2014 auto eventID = colPart.entryID;
2015 auto sourceID = colPart.sourceID;
2016 // enable embedding: if several colParts exist, then they are
2017 // saved as one collision
2018 if (nParts == 1 || sourceID == 0) {
2019 // FIXME:
2020 // use generators' names for generatorIDs (?)
2021 auto& header = mcReader->getMCEventHeader(sourceID, eventID);
2022 updateMCHeader(mcCollisionsCursor.cursor,
2023 hepmcXSectionsCursor.cursor,
2024 hepmcPdfInfosCursor.cursor,
2025 hepmcHeavyIonsCursor.cursor,
2026 header,
2027 iCol,
2028 bcID,
2029 time,
2030 0,
2031 sourceID);
2032 }
2033 mcColToEvSrc.emplace_back(std::vector<int>{iCol, sourceID, eventID}); // point background and injected signal events to one collision
2034 }
2035 }
2036 }
2037
2038 std::sort(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2039 [](const std::vector<int>& left, const std::vector<int>& right) { return (left[0] < right[0]); });
2040
2041 // vector of FDD amplitudes
2042 int16_t aFDDAmplitudesA[8] = {0u}, aFDDAmplitudesC[8] = {0u};
2043 float aFDDTimesA[8] = {0.f}, aFDDTimesC[8] = {0.f};
2044 // filling FDD table
2045 fddCursor.reserve(fddRecPoints.size());
2046 for (const auto& fddRecPoint : fddRecPoints) {
2047 for (int i = 0; i < 8; i++) {
2048 aFDDAmplitudesA[i] = 0;
2049 aFDDAmplitudesC[i] = 0;
2050 aFDDTimesA[i] = 0.f;
2051 aFDDTimesC[i] = 0.f;
2052 }
2053 uint64_t globalBC = fddRecPoint.getInteractionRecord().toLong();
2054 uint64_t bc = globalBC;
2055 auto item = bcsMap.find(bc);
2056 int bcID = -1;
2057 if (item != bcsMap.end()) {
2058 bcID = item->second;
2059 } else {
2060 LOG(fatal) << "Error: could not find a corresponding BC ID for a FDD rec. point; BC = " << bc;
2061 }
2062 const auto channelData = fddRecPoint.getBunchChannelData(fddChData);
2063 for (const auto& channel : channelData) {
2064 if (channel.mPMNumber < 8) {
2065 aFDDAmplitudesC[channel.mPMNumber] = channel.mChargeADC; // amplitude
2066 aFDDTimesC[channel.mPMNumber] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time
2067 } else {
2068 aFDDAmplitudesA[channel.mPMNumber - 8] = channel.mChargeADC; // amplitude
2069 aFDDTimesA[channel.mPMNumber - 8] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time
2070 }
2071 }
2072
2073 fddCursor(bcID,
2074 aFDDAmplitudesA,
2075 aFDDAmplitudesC,
2076 truncateFloatFraction(fddRecPoint.getCollisionTimeA() * 1E-3, mFDDTime), // ps to ns
2077 truncateFloatFraction(fddRecPoint.getCollisionTimeC() * 1E-3, mFDDTime), // ps to ns
2078 fddRecPoint.getTrigger().getTriggersignals());
2079 if (mEnableFITextra) {
2080 fddExtraCursor(bcID,
2081 aFDDTimesA,
2082 aFDDTimesC);
2083 }
2084 }
2085
2086 // filling FT0 table
2087 std::vector<float> aAmplitudesA, aAmplitudesC, aTimesA, aTimesC;
2088 std::vector<uint8_t> aChannelsA, aChannelsC;
2089 ft0Cursor.reserve(ft0RecPoints.size());
2090 for (auto& ft0RecPoint : ft0RecPoints) {
2091 aAmplitudesA.clear();
2092 aAmplitudesC.clear();
2093 aTimesA.clear();
2094 aTimesC.clear();
2095 aChannelsA.clear();
2096 aChannelsC.clear();
2097 const auto channelData = ft0RecPoint.getBunchChannelData(ft0ChData);
2098 for (auto& channel : channelData) {
2099 // TODO: switch to calibrated amplitude
2100 if (channel.QTCAmpl > 0) {
2101 constexpr int nFT0ChannelsAside = o2::ft0::Geometry::NCellsA * 4;
2102 if (channel.ChId < nFT0ChannelsAside) {
2103 aChannelsA.push_back(channel.ChId);
2104 aAmplitudesA.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude));
2105 aTimesA.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime));
2106 } else {
2107 aChannelsC.push_back(channel.ChId - nFT0ChannelsAside);
2108 aAmplitudesC.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude));
2109 aTimesC.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime));
2110 }
2111 }
2112 }
2113 uint64_t globalBC = ft0RecPoint.getInteractionRecord().toLong();
2114 uint64_t bc = globalBC;
2115 auto item = bcsMap.find(bc);
2116 int bcID = -1;
2117 if (item != bcsMap.end()) {
2118 bcID = item->second;
2119 } else {
2120 LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc;
2121 }
2122 ft0Cursor(bcID,
2123 aAmplitudesA,
2124 aChannelsA,
2125 aAmplitudesC,
2126 aChannelsC,
2127 truncateFloatFraction(ft0RecPoint.getCollisionTimeA() * 1E-3, mT0Time), // ps to ns
2128 truncateFloatFraction(ft0RecPoint.getCollisionTimeC() * 1E-3, mT0Time), // ps to ns
2129 ft0RecPoint.getTrigger().getTriggersignals());
2130 if (mEnableFITextra) {
2131 ft0ExtraCursor(bcID,
2132 aTimesA,
2133 aTimesC);
2134 }
2135 }
2136
2137 if (mUseMC) {
2138 // filling MC collision labels
2139 mcColLabelsCursor.reserve(primVerLabels.size());
2140 for (auto& label : primVerLabels) {
2141 auto it = std::find_if(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2142 [&label](const auto& mcColInfo) { return mcColInfo[1] == label.getSourceID() && mcColInfo[2] == label.getEventID(); });
2143 int32_t mcCollisionID = -1;
2144 if (it != mcColToEvSrc.end()) {
2145 mcCollisionID = it->at(0);
2146 }
2147 uint16_t mcMask = 0; // todo: set mask using normalized weights?
2148 mcColLabelsCursor(mcCollisionID, mcMask);
2149 }
2150 }
2151
2152 cacheTriggers(recoData);
2153 countTPCClusters(recoData);
2154
2155 int collisionID = 0;
2156 mIndexTableMFT.resize(recoData.getMFTTracks().size());
2157 mIndexTableFwd.resize(recoData.getMCHTracks().size());
2158
2159 auto& trackReffwd = primVer2TRefs.back();
2160 fillIndexTablesPerCollision(trackReffwd, primVerGIs, recoData);
2161 collisionID = 0;
2162 for (auto& vertex : primVertices) {
2163 auto& trackReffwd = primVer2TRefs[collisionID];
2164 fillIndexTablesPerCollision(trackReffwd, primVerGIs, recoData); // this function must follow the same track order as 'fillTrackTablesPerCollision' to fill the map of track indices
2165 collisionID++;
2166 }
2167
2169 prepareStrangenessTracking(recoData);
2170
2171 mGIDToTableFwdID.clear(); // reset the tables to be used by 'fillTrackTablesPerCollision'
2172 mGIDToTableMFTID.clear();
2173
2174 if (mPropTracks || mThinTracks) {
2175 auto v0s = recoData.getV0sIdx();
2176 auto cascades = recoData.getCascadesIdx();
2177 auto decays3Body = recoData.getDecays3BodyIdx();
2178 mGIDUsedBySVtx.reserve(v0s.size() * 2 + cascades.size() + decays3Body.size() * 3);
2179 for (const auto& v0 : v0s) {
2180 mGIDUsedBySVtx.insert(v0.getProngID(0));
2181 mGIDUsedBySVtx.insert(v0.getProngID(1));
2182 }
2183 for (const auto& cascade : cascades) {
2184 mGIDUsedBySVtx.insert(cascade.getBachelorID());
2185 }
2186 for (const auto& id3Body : decays3Body) {
2187 mGIDUsedBySVtx.insert(id3Body.getProngID(0));
2188 mGIDUsedBySVtx.insert(id3Body.getProngID(1));
2189 mGIDUsedBySVtx.insert(id3Body.getProngID(2));
2190 }
2191
2192 mGIDUsedByStr.reserve(recoData.getStrangeTracks().size());
2193 for (const auto& sTrk : recoData.getStrangeTracks()) {
2194 mGIDUsedByStr.emplace(sTrk.mITSRef, GIndex::ITS);
2195 }
2196 }
2197
2198 // filling unassigned tracks first
2199 // so that all unassigned tracks are stored in the beginning of the table together
2200 auto& trackRef = primVer2TRefs.back(); // references to unassigned tracks are at the end
2201 // fixme: interaction time is undefined for unassigned tracks (?)
2202 fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor,
2203 ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor,
2204 fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap);
2205
2206 // filling collisions and tracks into tables
2207 collisionID = 0;
2208 collisionsCursor.reserve(primVertices.size());
2209 for (auto& vertex : primVertices) {
2210 auto& cov = vertex.getCov();
2211 auto& timeStamp = vertex.getTimeStamp(); // this is a relative time
2212 const double interactionTime = timeStamp.getTimeStamp() * 1E3; // mus to ns
2213 uint64_t globalBC = relativeTime_to_GlobalBC(interactionTime);
2214 uint64_t localBC = relativeTime_to_LocalBC(interactionTime);
2215 LOG(debug) << "global BC " << globalBC << " local BC " << localBC << " relative interaction time " << interactionTime;
2216 // collision timestamp in ns wrt the beginning of collision BC
2217 const float relInteractionTime = static_cast<float>(localBC * o2::constants::lhc::LHCBunchSpacingNS - interactionTime);
2218 auto item = bcsMap.find(globalBC);
2219 int bcID = -1;
2220 if (item != bcsMap.end()) {
2221 bcID = item->second;
2222 } else {
2223 LOG(fatal) << "Error: could not find a corresponding BC ID for a collision; BC = " << globalBC << ", collisionID = " << collisionID;
2224 }
2225 collisionsCursor(bcID,
2226 truncateFloatFraction(vertex.getX(), mCollisionPosition),
2227 truncateFloatFraction(vertex.getY(), mCollisionPosition),
2228 truncateFloatFraction(vertex.getZ(), mCollisionPosition),
2229 truncateFloatFraction(cov[0], mCollisionPositionCov),
2230 truncateFloatFraction(cov[1], mCollisionPositionCov),
2231 truncateFloatFraction(cov[2], mCollisionPositionCov),
2232 truncateFloatFraction(cov[3], mCollisionPositionCov),
2233 truncateFloatFraction(cov[4], mCollisionPositionCov),
2234 truncateFloatFraction(cov[5], mCollisionPositionCov),
2235 vertex.getFlags(),
2236 truncateFloatFraction(vertex.getChi2(), mCollisionPositionCov),
2237 vertex.getNContributors(),
2238 truncateFloatFraction(relInteractionTime, mCollisionPosition),
2239 truncateFloatFraction(timeStamp.getTimeStampError() * 1E3, mCollisionPositionCov));
2240 mVtxToTableCollID[collisionID] = mTableCollID++;
2241
2242 auto& trackRef = primVer2TRefs[collisionID];
2243 // passing interaction time in [ps]
2244 fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, ambigTracksCursor,
2245 mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor,
2246 fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap);
2247 collisionID++;
2248 }
2249
2250 fillSecondaryVertices(recoData, v0sCursor, cascadesCursor, decay3BodyCursor);
2251 fillHMPID(recoData, hmpCursor);
2252 fillStrangenessTrackingTables(recoData, trackedV0Cursor, trackedCascadeCursor, tracked3BodyCurs);
2253
2254 // helper map for fast search of a corresponding class mask for a bc
2255 auto emcalIncomplete = filterEMCALIncomplete(recoData.getEMCALTriggers());
2256 std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> bcToClassMask;
2257 if (mInputSources[GID::CTP]) {
2258 LOG(debug) << "CTP input available";
2259 for (auto& ctpDigit : ctpDigits) {
2260 uint64_t bc = ctpDigit.intRecord.toLong();
2261 uint64_t classMask = ctpDigit.CTPClassMask.to_ulong();
2262 uint64_t inputMask = ctpDigit.CTPInputMask.to_ulong();
2263 if (emcalIncomplete.find(bc) != emcalIncomplete.end()) {
2264 // reject EMCAL triggers as BC was rejected as incomplete at readout level
2265 auto classMaskOrig = classMask;
2266 classMask = classMask & ~mEMCALTrgClassMask;
2267 LOG(debug) << "Found EMCAL incomplete event, mask before " << std::bitset<64>(classMaskOrig) << ", after " << std::bitset<64>(classMask);
2268 }
2269 bcToClassMask[bc] = {classMask, inputMask};
2270 // LOG(debug) << Form("classmask:0x%llx", classMask);
2271 }
2272 }
2273
2274 // filling BC table
2275 bcCursor.reserve(bcsMap.size());
2276 for (auto& item : bcsMap) {
2277 uint64_t bc = item.first;
2278 std::pair<uint64_t, uint64_t> masks{0, 0};
2279 if (mInputSources[GID::CTP]) {
2280 auto bcClassPair = bcToClassMask.find(bc);
2281 if (bcClassPair != bcToClassMask.end()) {
2282 masks = bcClassPair->second;
2283 }
2284 }
2285 bcCursor(runNumber,
2286 bc,
2287 masks.first,
2288 masks.second);
2289 }
2290
2291 bcToClassMask.clear();
2292
2293 // filling BC flags table:
2294 auto bcFlags = fillBCFlags(recoData, bcsMap);
2295 bcFlagsCursor.reserve(bcFlags.size());
2296 for (auto f : bcFlags) {
2297 bcFlagsCursor(f);
2298 }
2299
2300 // fill cpvcluster table
2301 if (mInputSources[GIndex::CPV]) {
2302 float posX, posZ;
2303 cpvClustersCursor.reserve(cpvTrigRecs.size());
2304 for (auto& cpvEvent : cpvTrigRecs) {
2305 uint64_t bc = cpvEvent.getBCData().toLong();
2306 auto item = bcsMap.find(bc);
2307 int bcID = -1;
2308 if (item != bcsMap.end()) {
2309 bcID = item->second;
2310 } else {
2311 LOG(fatal) << "Error: could not find a corresponding BC ID for a CPV Trigger Record; BC = " << bc;
2312 }
2313 for (int iClu = cpvEvent.getFirstEntry(); iClu < cpvEvent.getFirstEntry() + cpvEvent.getNumberOfObjects(); iClu++) {
2314 auto& clu = cpvClusters[iClu];
2315 clu.getLocalPosition(posX, posZ);
2316 cpvClustersCursor(bcID,
2317 truncateFloatFraction(posX, mCPVPos),
2318 truncateFloatFraction(posZ, mCPVPos),
2319 truncateFloatFraction(clu.getEnergy(), mCPVAmpl),
2320 clu.getPackedClusterStatus());
2321 }
2322 }
2323 }
2324
2325 if (mUseMC) {
2326 TStopwatch timer;
2327 timer.Start();
2328 // filling mc particles table
2329 fillMCParticlesTable(*mcReader,
2330 mcParticlesCursor.cursor,
2331 primVer2TRefs,
2332 primVerGIs,
2333 recoData,
2334 mcColToEvSrc);
2335 timer.Stop();
2336 LOG(info) << "FILL MC took " << timer.RealTime() << " s";
2337 mcColToEvSrc.clear();
2338
2339 // ------------------------------------------------------
2340 // filling track labels
2341
2342 // need to go through labels in the same order as for tracks
2343 fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, primVer2TRefs.back(), primVerGIs, recoData);
2344 for (auto iref = 0U; iref < primVer2TRefs.size() - 1; iref++) {
2345 auto& trackRef = primVer2TRefs[iref];
2346 fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, trackRef, primVerGIs, recoData, iref);
2347 }
2348 }
2349
2350 // Fill calo tables and if MC also the MCCaloTable, therefore, has to be after fillMCParticlesTable call!
2351 if (mInputSources[GIndex::PHS] || mInputSources[GIndex::EMC]) {
2352 fillCaloTable(caloCellsCursor, caloCellsTRGTableCursor, mcCaloLabelsCursor, bcsMap, recoData);
2353 }
2354
2355 bcsMap.clear();
2356 clearMCKeepStore(mToStore);
2357 mGIDToTableID.clear();
2358 mTableTrID = 0;
2359 mGIDToTableFwdID.clear();
2360 mTableTrFwdID = 0;
2361 mGIDToTableMFTID.clear();
2362 mTableTrMFTID = 0;
2363 mVtxToTableCollID.clear();
2364 mTableCollID = 0;
2365 mV0ToTableID.clear();
2366 mTableV0ID = 0;
2367
2368 mIndexTableFwd.clear();
2369 mIndexFwdID = 0;
2370 mIndexTableMFT.clear();
2371 mIndexMFTID = 0;
2372
2373 mBCLookup.clear();
2374
2375 mGIDUsedBySVtx.clear();
2376 mGIDUsedByStr.clear();
2377
2378 originCursor(tfNumber);
2379
2380 // sending metadata to writer
2381 TString dataType = mUseMC ? "MC" : "RAW";
2382 TString O2Version = o2::fullVersion();
2383 TString ROOTVersion = ROOT_RELEASE;
2384 mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag"};
2385 mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag};
2386 pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, mMetaDataKeys);
2387 pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, mMetaDataVals);
2388
2389 pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber);
2390 pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, "");
2391
2392 mTimer.Stop();
2393}
2394
2395void AODProducerWorkflowDPL::cacheTriggers(const o2::globaltracking::RecoContainer& recoData)
2396{
2397 // ITS tracks->ROF
2398 {
2399 mITSROFs.clear();
2400 const auto& rofs = recoData.getITSTracksROFRecords();
2401 uint16_t count = 0;
2402 for (const auto& rof : rofs) {
2403 int first = rof.getFirstEntry(), last = first + rof.getNEntries();
2404 for (int i = first; i < last; i++) {
2405 mITSROFs.push_back(count);
2406 }
2407 count++;
2408 }
2409 }
2410 // MFT tracks->ROF
2411 {
2412 mMFTROFs.clear();
2413 const auto& rofs = recoData.getMFTTracksROFRecords();
2414 uint16_t count = 0;
2415 for (const auto& rof : rofs) {
2416 int first = rof.getFirstEntry(), last = first + rof.getNEntries();
2417 for (int i = first; i < last; i++) {
2418 mMFTROFs.push_back(count);
2419 }
2420 count++;
2421 }
2422 }
2423 // ITSTPCTRD tracks -> TRD trigger
2424 {
2425 mITSTPCTRDTriggers.clear();
2426 const auto& itstpctrigs = recoData.getITSTPCTRDTriggers();
2427 int count = 0;
2428 for (const auto& trig : itstpctrigs) {
2429 int first = trig.getFirstTrack(), last = first + trig.getNumberOfTracks();
2430 for (int i = first; i < last; i++) {
2431 mITSTPCTRDTriggers.push_back(count);
2432 }
2433 count++;
2434 }
2435 }
2436 // TPCTRD tracks -> TRD trigger
2437 {
2438 mTPCTRDTriggers.clear();
2439 const auto& tpctrigs = recoData.getTPCTRDTriggers();
2440 int count = 0;
2441 for (const auto& trig : tpctrigs) {
2442 int first = trig.getFirstTrack(), last = first + trig.getNumberOfTracks();
2443 for (int i = first; i < last; i++) {
2444 mTPCTRDTriggers.push_back(count);
2445 }
2446 count++;
2447 }
2448 }
2449 // MCH tracks->ROF
2450 {
2451 mMCHROFs.clear();
2452 const auto& rofs = recoData.getMCHTracksROFRecords();
2453 uint16_t count = 0;
2454 for (const auto& rof : rofs) {
2455 int first = rof.getFirstIdx(), last = first + rof.getNEntries();
2456 for (int i = first; i < last; i++) {
2457 mMCHROFs.push_back(count);
2458 }
2459 count++;
2460 }
2461 }
2462}
2463
2464AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrack(int collisionID, std::uint64_t collisionBC, GIndex trackIndex,
2465 const o2::globaltracking::RecoContainer& data, const std::map<uint64_t, int>& bcsMap)
2466{
2467 TrackExtraInfo extraInfoHolder;
2468 if (collisionID < 0) {
2469 extraInfoHolder.flags |= o2::aod::track::OrphanTrack;
2470 }
2471 bool needBCSlice = collisionID < 0; // track is associated to multiple vertices
2472 uint64_t bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default track time is wrt collision BC (unless no collision assigned)
2473
2474 auto setTrackTime = [&](double t, double terr, bool gaussian) {
2475 // set track time and error, for ambiguous tracks define the bcSlice as it was used in vertex-track association
2476 // provided track time (wrt TF start) and its error should be in ns, gaussian flag tells if the error is assumed to be gaussin or half-interval
2477 if (!gaussian) {
2478 extraInfoHolder.flags |= o2::aod::track::TrackTimeResIsRange;
2479 }
2480 extraInfoHolder.trackTimeRes = terr;
2481 if (needBCSlice) { // need to define BC slice
2482 double error = this->mTimeMarginTrackTime + (gaussian ? extraInfoHolder.trackTimeRes * this->mNSigmaTimeTrack : extraInfoHolder.trackTimeRes);
2483 bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - error, t + error, bcsMap);
2484 }
2485 extraInfoHolder.trackTime = float(t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS);
2486 extraInfoHolder.diffBCRef = int(bcOfTimeRef);
2487 LOGP(debug, "time : {}/{} -> {}/{} -> trunc: {}/{} CollID: {} Amb: {}", t, terr, t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS, terr,
2488 truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime), truncateFloatFraction(extraInfoHolder.trackTimeRes, mTrackTimeError),
2489 collisionID, trackIndex.isAmbiguous());
2490 };
2491 auto contributorsGID = data.getSingleDetectorRefs(trackIndex);
2492 const auto& trackPar = data.getTrackParam(trackIndex);
2493 extraInfoHolder.flags |= trackPar.getPID() << 28;
2494 auto src = trackIndex.getSource();
2495 if (contributorsGID[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF
2496 const auto& tofMatch = data.getTOFMatch(trackIndex);
2497 extraInfoHolder.tofChi2 = tofMatch.getChi2();
2498 const auto& tofInt = tofMatch.getLTIntegralOut();
2499 float intLen = tofInt.getL();
2500 extraInfoHolder.length = intLen;
2501 const float mass = o2::constants::physics::MassPionCharged; // default pid = pion
2502 if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) {
2503 float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed));
2504 if (expBeta > o2::constants::math::Almost1) {
2506 }
2507 extraInfoHolder.tofExpMom = mass * expBeta / std::sqrt(1.f - expBeta * expBeta);
2508 }
2509 // correct the time of the track
2510 const double massZ = o2::track::PID::getMass2Z(trackPar.getPID());
2511 const double energy = sqrt((massZ * massZ) + (extraInfoHolder.tofExpMom * extraInfoHolder.tofExpMom));
2512 const double exp = extraInfoHolder.length * energy / (cSpeed * extraInfoHolder.tofExpMom);
2513 auto tofSignal = (tofMatch.getSignal() - exp) * 1e-3; // time in ns wrt TF start
2514 setTrackTime(tofSignal, 0.2, true); // FIXME: calculate actual resolution (if possible?)
2515 }
2516 if (contributorsGID[GIndex::Source::TRD].isIndexSet()) { // ITS-TPC-TRD-TOF, TPC-TRD-TOF, TPC-TRD, ITS-TPC-TRD
2517 const auto& trdOrig = data.getTrack<o2::trd::TrackTRD>(contributorsGID[GIndex::Source::TRD]); // refitted TRD trac
2518 extraInfoHolder.trdChi2 = trdOrig.getChi2();
2519 extraInfoHolder.trdSignal = trdOrig.getSignal();
2520 extraInfoHolder.trdPattern = getTRDPattern(trdOrig);
2521 if (extraInfoHolder.trackTimeRes < 0.) { // time is not set yet, this is possible only for TPC-TRD and ITS-TPC-TRD tracks, since those with TOF are set upstream
2522 // TRD is triggered: time uncertainty is within a BC
2523 const auto& trdTrig = (src == GIndex::Source::TPCTRD) ? data.getTPCTRDTriggers()[mTPCTRDTriggers[trackIndex.getIndex()]] : data.getITSTPCTRDTriggers()[mITSTPCTRDTriggers[trackIndex.getIndex()]];
2524 double ttrig = trdTrig.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS; // 1st get time wrt TF start
2525 setTrackTime(ttrig, 1., true); // FIXME: calculate actual resolution (if possible?)
2526 }
2527 }
2528 if (contributorsGID[GIndex::Source::ITS].isIndexSet()) {
2529 const auto& itsTrack = data.getITSTrack(contributorsGID[GIndex::ITS]);
2530 int nClusters = itsTrack.getNClusters();
2531 float chi2 = itsTrack.getChi2();
2532 extraInfoHolder.itsChi2NCl = nClusters != 0 ? chi2 / (float)nClusters : 0;
2533 extraInfoHolder.itsClusterSizes = itsTrack.getClusterSizes();
2534 if (src == GIndex::ITS) { // standalone ITS track should set its time from the ROF
2535 const auto& rof = data.getITSTracksROFRecords()[mITSROFs[trackIndex.getIndex()]];
2536 double t = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mITSROFrameHalfLengthNS + mITSROFBiasNS;
2537 setTrackTime(t, mITSROFrameHalfLengthNS, false);
2538 }
2539 } else if (contributorsGID[GIndex::Source::ITSAB].isIndexSet()) { // this is an ITS-TPC afterburner contributor
2540 extraInfoHolder.itsClusterSizes = data.getITSABRefs()[contributorsGID[GIndex::Source::ITSAB].getIndex()].getClusterSizes();
2541 }
2542 if (contributorsGID[GIndex::Source::TPC].isIndexSet()) {
2543 const auto& tpcOrig = data.getTPCTrack(contributorsGID[GIndex::TPC]);
2544 const auto& tpcClData = mTPCCounters[contributorsGID[GIndex::TPC]];
2545 extraInfoHolder.tpcInnerParam = tpcOrig.getP() / tpcOrig.getAbsCharge();
2546 extraInfoHolder.tpcChi2NCl = tpcOrig.getNClusters() ? tpcOrig.getChi2() / tpcOrig.getNClusters() : 0;
2547 extraInfoHolder.tpcSignal = tpcOrig.getdEdx().dEdxTotTPC;
2548 extraInfoHolder.tpcNClsFindable = tpcOrig.getNClusters();
2549 extraInfoHolder.tpcNClsFindableMinusFound = tpcOrig.getNClusters() - tpcClData.found;
2550 extraInfoHolder.tpcNClsFindableMinusCrossedRows = tpcOrig.getNClusters() - tpcClData.crossed;
2551 extraInfoHolder.tpcNClsShared = tpcClData.shared;
2552 uint32_t clsUsedForPID = tpcOrig.getdEdx().NHitsIROC + tpcOrig.getdEdx().NHitsOROC1 + tpcOrig.getdEdx().NHitsOROC2 + tpcOrig.getdEdx().NHitsOROC3;
2553 extraInfoHolder.tpcNClsFindableMinusPID = tpcOrig.getNClusters() - clsUsedForPID;
2554 if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range
2555 if (needBCSlice) {
2556 double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value
2557 double terr = 0.5 * (tpcOrig.getDeltaTFwd() + tpcOrig.getDeltaTBwd()) * mTPCBinNS;
2558 double err = mTimeMarginTrackTime + terr;
2559 bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - err, t + err, bcsMap);
2560 }
2562 p.setDeltaTFwd(tpcOrig.getDeltaTFwd());
2563 p.setDeltaTBwd(tpcOrig.getDeltaTBwd());
2564 extraInfoHolder.trackTimeRes = p.getTimeErr();
2565 extraInfoHolder.trackTime = float(tpcOrig.getTime0() * mTPCBinNS - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS);
2566 extraInfoHolder.diffBCRef = int(bcOfTimeRef);
2567 extraInfoHolder.isTPConly = true; // no truncation
2568 extraInfoHolder.flags |= o2::aod::track::TrackTimeAsym;
2569 } else if (src == GIndex::ITSTPC) { // its-tpc matched tracks have gaussian time error and the time was not set above
2570 const auto& trITSTPC = data.getTPCITSTrack(trackIndex);
2571 auto ts = trITSTPC.getTimeMUS();
2572 setTrackTime(ts.getTimeStamp() * 1.e3, ts.getTimeStampError() * 1.e3, true);
2573 }
2574 }
2575
2576 extrapolateToCalorimeters(extraInfoHolder, data.getTrackParamOut(trackIndex));
2577 // set bit encoding for PVContributor property as part of the flag field
2578 if (trackIndex.isPVContributor()) {
2579 extraInfoHolder.flags |= o2::aod::track::PVContributor;
2580 }
2581 return extraInfoHolder;
2582}
2583
2584AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int collisionID, std::uint64_t collisionBC, GIndex trackIndex,
2585 const o2::globaltracking::RecoContainer& data, const std::map<uint64_t, int>& bcsMap)
2586{
2587 TrackQA trackQAHolder;
2588 auto contributorsGID = data.getTPCContributorGID(trackIndex);
2589 const auto& trackPar = data.getTrackParam(trackIndex);
2590 if (contributorsGID.isIndexSet()) {
2591 auto prop = o2::base::Propagator::Instance();
2592 const auto& tpcOrig = data.getTPCTrack(contributorsGID);
2595 const o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT;
2596 const o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ());
2597 o2::gpu::gpustd::array<float, 2> dcaInfo{-999., -999.};
2598 if (prop->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) {
2599 trackQAHolder.tpcdcaR = 100. * dcaInfo[0] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt());
2600 trackQAHolder.tpcdcaZ = 100. * dcaInfo[1] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt());
2601 }
2602 // This allows to safely clamp any float to one byte, using the
2603 // minmal/maximum values as under-/overflow borders and rounding to the nearest integer
2604 auto safeInt8Clamp = [](auto value) -> int8_t {
2605 using ValType = decltype(value);
2606 return static_cast<int8_t>(TMath::Nint(std::clamp(value, static_cast<ValType>(std::numeric_limits<int8_t>::min()), static_cast<ValType>(std::numeric_limits<int8_t>::max()))));
2607 };
2609 uint8_t clusterCounters[8] = {0};
2610 {
2611 uint8_t sectorIndex, rowIndex;
2612 uint32_t clusterIndex;
2613 const auto& tpcClusRefs = data.getTPCTracksClusterRefs();
2614 for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) {
2615 o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, tpcOrig.getClusterRef());
2616 char indexTracklet = (rowIndex % 152) / 19;
2617 clusterCounters[indexTracklet]++;
2618 }
2619 }
2620 uint8_t byteMask = 0;
2621 for (int i = 0; i < 8; i++) {
2622 if (clusterCounters[i] > 5) {
2623 byteMask |= (1 << i);
2624 }
2625 }
2626 trackQAHolder.tpcTime0 = tpcOrig.getTime0();
2627 trackQAHolder.tpcClusterByteMask = byteMask;
2628 const float dEdxNorm = (tpcOrig.getdEdx().dEdxTotTPC > 0) ? 100. / tpcOrig.getdEdx().dEdxTotTPC : 0;
2629 trackQAHolder.tpcdEdxMax0R = uint8_t(tpcOrig.getdEdx().dEdxMaxIROC * dEdxNorm);
2630 trackQAHolder.tpcdEdxMax1R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC1 * dEdxNorm);
2631 trackQAHolder.tpcdEdxMax2R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC2 * dEdxNorm);
2632 trackQAHolder.tpcdEdxMax3R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC3 * dEdxNorm);
2633 //
2634 trackQAHolder.tpcdEdxTot0R = uint8_t(tpcOrig.getdEdx().dEdxTotIROC * dEdxNorm);
2635 trackQAHolder.tpcdEdxTot1R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC1 * dEdxNorm);
2636 trackQAHolder.tpcdEdxTot2R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC2 * dEdxNorm);
2637 trackQAHolder.tpcdEdxTot3R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC3 * dEdxNorm);
2639 float scaleTOF{0};
2640 auto contributorsGIDA = data.getSingleDetectorRefs(trackIndex);
2641 if (contributorsGIDA[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF
2642 const auto& tofMatch = data.getTOFMatch(trackIndex);
2643 const float qpt = trackPar.getQ2Pt();
2645 trackQAHolder.dTofdX = safeInt8Clamp(tofMatch.getDXatTOF() / scaleTOF);
2646 trackQAHolder.dTofdZ = safeInt8Clamp(tofMatch.getDZatTOF() / scaleTOF);
2647 }
2648
2649 // Add matching information at a reference point (defined by
2650 // o2::aod::track::trackQARefRadius) in the same frame as the global track
2651 // without material corrections and error propagation
2652 if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) {
2653 const auto& itsOrig = data.getITSTrack(itsContGID);
2654 o2::track::TrackPar gloCopy = trackPar;
2655 o2::track::TrackPar itsCopy = itsOrig;
2656 o2::track::TrackPar tpcCopy = tpcOrig;
2657 if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) &&
2658 prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) &&
2659 prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) {
2660 // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track
2661 // The scale is defined by the global track scaling depending on beta0
2662 const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f));
2663 const float qpt = gloCopy.getQ2Pt();
2664 const float x = qpt / beta0;
2665 // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2)
2666 auto scaleCont = [&x](int i) -> float {
2668 };
2669 auto scaleGlo = [&x](int i) -> float {
2671 };
2672
2673 // Calculate deltas for contributors
2674 trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0));
2675 trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1));
2676 trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2));
2677 trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3));
2678 trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4));
2679 // Calculate deltas for global track against averaged contributors
2680 trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0));
2681 trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1));
2682 trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2));
2683 trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3));
2684 trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4));
2685 //
2686
2687 if (mStreamerFlags[AODProducerStreamerFlags::TrackQA]) {
2688 (*mStreamer) << "trackQA"
2689 << "trackITSOrig=" << itsOrig
2690 << "trackTPCOrig=" << tpcOrig
2691 << "trackITSTPCOrig=" << trackPar
2692 << "trackITSProp=" << itsCopy
2693 << "trackTPCProp=" << tpcCopy
2694 << "trackITSTPCProp=" << gloCopy
2695 << "refRadius=" << o2::aod::track::trackQARefRadius
2696 << "scaleBins=" << o2::aod::track::trackQAScaleBins
2697 << "scaleCont0=" << scaleCont(0)
2698 << "scaleCont1=" << scaleCont(1)
2699 << "scaleCont2=" << scaleCont(2)
2700 << "scaleCont3=" << scaleCont(3)
2701 << "scaleCont4=" << scaleCont(4)
2702 << "scaleGlo0=" << scaleGlo(0)
2703 << "scaleGlo1=" << scaleGlo(1)
2704 << "scaleGlo2=" << scaleGlo(2)
2705 << "scaleGlo3=" << scaleGlo(3)
2706 << "scaleGlo4=" << scaleGlo(4)
2707 << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0
2708 << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR
2709 << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ
2710 << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask
2711 << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R
2712 << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R
2713 << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R
2714 << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R
2715 << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R
2716 << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R
2717 << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R
2718 << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R
2719 << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY
2720 << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ
2721 << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp
2722 << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl
2723 << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt
2724 << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY
2725 << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ
2726 << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp
2727 << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl
2728 << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt
2729 << "trackQAHolder.dTofdX=" << trackQAHolder.dTofdX
2730 << "trackQAHolder.dTofdZ=" << trackQAHolder.dTofdZ
2731 << "scaleTOF=" << scaleTOF
2732 << "\n";
2733 }
2734 }
2735 }
2736 }
2737
2738 return trackQAHolder;
2739}
2740
2741bool AODProducerWorkflowDPL::propagateTrackToPV(o2::track::TrackParametrizationWithError<float>& trackPar,
2743 int colID)
2744{
2745 o2::dataformats::DCA dcaInfo;
2746 dcaInfo.set(999.f, 999.f, 999.f, 999.f, 999.f);
2747 o2::dataformats::VertexBase v = mVtx.getMeanVertex(colID < 0 ? 0.f : data.getPrimaryVertex(colID).getZ());
2748 return o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trackPar, 2.f, mMatCorr, &dcaInfo);
2749}
2750
2751void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfoHolder, const o2::track::TrackPar& track)
2752{
2753 constexpr float XEMCAL = 440.f, XPHOS = 460.f, XEMCAL2 = XEMCAL * XEMCAL;
2754 constexpr float ETAEMCAL = 0.75; // eta of EMCAL/DCAL with margin
2755 constexpr float ZEMCALFastCheck = 460.; // Max Z (with margin to check with straightline extrapolarion)
2756 constexpr float ETADCALINNER = 0.22; // eta of the DCAL PHOS Hole (at XEMCAL)
2757 constexpr float ETAPHOS = 0.13653194; // nominal eta of the PHOS acceptance (at XPHOS): -log(tan((TMath::Pi()/2 - atan2(63, 460))/2))
2758 constexpr float ETAPHOSMARGIN = 0.17946979; // etat of the PHOS acceptance with 20 cm margin (at XPHOS): -log(tan((TMath::Pi()/2 + atan2(63+20., 460))/2)), not used, for the ref only
2759 constexpr float ETADCALPHOSSWITCH = (ETADCALINNER + ETAPHOS) / 2; // switch to DCAL to PHOS check if eta < this value
2760 constexpr short SNONE = 0, SEMCAL = 0x1, SPHOS = 0x2;
2761 constexpr short SECTORTYPE[18] = {
2762 SNONE, SNONE, SNONE, SNONE, // 0:3
2763 SEMCAL, SEMCAL, SEMCAL, SEMCAL, SEMCAL, SEMCAL, // 3:9 EMCAL only
2764 SNONE, SNONE, // 10:11
2765 SPHOS, // 12 PHOS only
2766 SPHOS | SEMCAL, SPHOS | SEMCAL, SPHOS | SEMCAL, // 13:15 PHOS & DCAL
2767 SEMCAL, // 16 DCAL only
2768 SNONE // 17
2769 };
2770
2771 o2::track::TrackPar outTr{track};
2772 auto prop = o2::base::Propagator::Instance();
2773 // 1st propagate to EMCAL nominal radius
2774 float xtrg = 0;
2775 // quick check with straight line propagtion
2776 if (!outTr.getXatLabR(XEMCAL, xtrg, prop->getNominalBz(), o2::track::DirType::DirOutward) ||
2777 (std::abs(outTr.getZAt(xtrg, 0)) > ZEMCALFastCheck) ||
2778 !prop->PropagateToXBxByBz(outTr, xtrg, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT)) {
2779 LOGP(debug, "preliminary step: does not reach R={} {}", XEMCAL, outTr.asString());
2780 return;
2781 }
2782 // we do not necessarilly reach wanted radius in a single propagation
2783 if ((outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY() < XEMCAL2) &&
2784 (!outTr.rotateParam(outTr.getPhi()) ||
2785 !outTr.getXatLabR(XEMCAL, xtrg, prop->getNominalBz(), o2::track::DirType::DirOutward) ||
2786 !prop->PropagateToXBxByBz(outTr, xtrg, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT))) {
2787 LOGP(debug, "does not reach R={} {}", XEMCAL, outTr.asString());
2788 return;
2789 }
2790 // rotate to proper sector
2791 int sector = o2::math_utils::angle2Sector(outTr.getPhiPos());
2792
2793 auto propExactSector = [&outTr, &sector, prop](float xprop) -> bool { // propagate exactly to xprop in the proper sector frame
2794 int ntri = 0;
2795 while (ntri < 2) {
2796 auto outTrTmp = outTr;
2797 float alpha = o2::math_utils::sector2Angle(sector);
2798 if ((std::abs(outTr.getZ()) > ZEMCALFastCheck) || !outTrTmp.rotateParam(alpha) ||
2799 !prop->PropagateToXBxByBz(outTrTmp, xprop, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT)) {
2800 LOGP(debug, "failed on rotation to {} (sector {}) or propagation to X={} {}", alpha, sector, xprop, outTrTmp.asString());
2801 return false;
2802 }
2803 // make sure we are still in the target sector
2804 int sectorTmp = o2::math_utils::angle2Sector(outTrTmp.getPhiPos());
2805 if (sectorTmp == sector) {
2806 outTr = outTrTmp;
2807 break;
2808 }
2809 sector = sectorTmp;
2810 ntri++;
2811 }
2812 if (ntri == 2) {
2813 LOGP(debug, "failed to rotate to sector, {}", outTr.asString());
2814 return false;
2815 }
2816 return true;
2817 };
2818
2819 // we are at the EMCAL X, check if we are in the good sector
2820 if (!propExactSector(XEMCAL) || SECTORTYPE[sector] == SNONE) { // propagation failed or neither EMCAL not DCAL/PHOS
2821 return;
2822 }
2823
2824 // check if we are in a good eta range
2825 float r = std::sqrt(outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY()), tg = std::atan2(r, outTr.getZ());
2826 float eta = -std::log(std::tan(0.5f * tg)), etaAbs = std::abs(eta);
2827 if (etaAbs > ETAEMCAL) {
2828 LOGP(debug, "eta = {} is off at EMCAL radius", eta, outTr.asString());
2829 return;
2830 }
2831 // are we in the PHOS hole (with margin)?
2832 if ((SECTORTYPE[sector] & SPHOS) && etaAbs < ETADCALPHOSSWITCH) { // propagate to PHOS radius
2833 if (!propExactSector(XPHOS)) {
2834 return;
2835 }
2836 r = std::sqrt(outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY());
2837 tg = std::atan2(r, outTr.getZ());
2838 eta = -std::log(std::tan(0.5f * tg));
2839 } else if (!(SECTORTYPE[sector] & SEMCAL)) { // are in the sector with PHOS only
2840 return;
2841 }
2842 extraInfoHolder.trackPhiEMCAL = outTr.getPhiPos();
2843 extraInfoHolder.trackEtaEMCAL = eta;
2844 LOGP(debug, "eta = {} phi = {} sector {} for {}", extraInfoHolder.trackEtaEMCAL, extraInfoHolder.trackPhiEMCAL, sector, outTr.asString());
2845 //
2846}
2847
2848std::set<uint64_t> AODProducerWorkflowDPL::filterEMCALIncomplete(const gsl::span<const o2::emcal::TriggerRecord> triggers)
2849{
2850 std::set<uint64_t> emcalIncompletes;
2851 for (const auto& trg : triggers) {
2852 if (trg.getTriggerBits() & o2::emcal::triggerbits::Inc) {
2853 // trigger record masked at incomplete at readout level
2854 emcalIncompletes.insert(trg.getBCData().toLong());
2855 }
2856 }
2857 return emcalIncompletes;
2858}
2859
2860void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc)
2861{
2863 static bool initOnceDone = false;
2864 if (!initOnceDone) { // this params need to be queried only once
2865 initOnceDone = true;
2866 // Note: DPLAlpideParam for ITS and MFT will be loaded by the RecoContainer
2867 mSqrtS = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS();
2868 // apply settings
2871 std::bitset<3564> bs = bcf.getBCPattern();
2872 for (auto i = 0U; i < bs.size(); i++) {
2873 if (bs.test(i)) {
2875 }
2876 }
2877
2879 mITSROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsITS.roFrameLengthTrig);
2882 mMFTROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsMFT.roFrameLengthTrig);
2884
2885 // RS FIXME: this is not yet fetched from the CCDB
2887 mTPCBinNS = elParam.ZbinWidth * 1.e3;
2888
2889 const auto& pvParams = o2::vertexing::PVertexerParams::Instance();
2890 mNSigmaTimeTrack = pvParams.nSigmaTimeTrack;
2891 mTimeMarginTrackTime = pvParams.timeMarginTrackTime * 1.e3;
2892 mFieldON = std::abs(o2::base::Propagator::Instance()->getNominalBz()) > 0.01;
2893
2894 pc.inputs().get<o2::ctp::CTPConfiguration*>("ctpconfig");
2895 }
2896 if (mPropTracks) {
2898 }
2899}
2900
2901//_______________________________________
2903{
2904 // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level
2905 if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) {
2906 if (matcher == ConcreteDataMatcher("GLO", "GRPMAGFIELD", 0)) {
2908 }
2909 return;
2910 }
2911 if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) {
2912 LOG(info) << "ITS Alpide param updated";
2914 par.printKeyValues();
2915 return;
2916 }
2917 if (matcher == ConcreteDataMatcher("MFT", "ALPIDEPARAM", 0)) {
2918 LOG(info) << "MFT Alpide param updated";
2920 par.printKeyValues();
2921 return;
2922 }
2923 if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) {
2924 LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString();
2925 mVtx = *(const o2::dataformats::MeanVertexObject*)obj;
2926 return;
2927 }
2928 if (matcher == ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) {
2929 // construct mask with EMCAL trigger classes for rejection of incomplete triggers
2930 auto ctpconfig = *(const o2::ctp::CTPConfiguration*)obj;
2931 mEMCALTrgClassMask = 0;
2932 for (const auto& trgclass : ctpconfig.getCTPClasses()) {
2933 if (trgclass.cluster->maskCluster[o2::detectors::DetID::EMC]) {
2934 mEMCALTrgClassMask |= trgclass.classMask;
2935 }
2936 }
2937 LOG(info) << "Loaded EMCAL trigger class mask: " << std::bitset<64>(mEMCALTrgClassMask);
2938 }
2939}
2940
2941void AODProducerWorkflowDPL::addRefGlobalBCsForTOF(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span<const GIndex>& GIndices,
2942 const o2::globaltracking::RecoContainer& data, std::map<uint64_t, int>& bcsMap)
2943{
2944 // Orphan tracks need to refer to some globalBC and for tracks with TOF this BC should be whithin an orbit
2945 // from the track abs time (to guarantee time precision). Therefore, we may need to insert some dummy globalBCs
2946 // to guarantee proper reference.
2947 // complete globalBCs by dummy entries necessary to provide BC references for TOF tracks with requested precision
2948 // to provide a reference for the time of the orphan tracks we should make sure that there are no gaps longer
2949 // than needed to store the time with sufficient precision
2950
2951 // estimate max distance in BCs between TOF time and eventual reference BC
2952 int nbitsFrac = 24 - (32 - o2::math_utils::popcount(mTrackTime)); // number of bits used to encode the fractional part of float truncated by the mask
2953 int nbitsLoss = std::max(0, int(std::log2(TOFTimePrecPS))); // allowed bit loss guaranteeing needed precision in PS
2954 assert(nbitsFrac > 1);
2955 std::uint64_t maxRangePS = std::uint64_t(0x1) << (nbitsFrac + nbitsLoss);
2956 int maxGapBC = maxRangePS / (o2::constants::lhc::LHCBunchSpacingNS * 1e3); // max gap in BCs allowing to store time with required precision
2957 LOG(info) << "Max gap of " << maxGapBC << " BCs to closest globalBC reference is needed for TOF tracks to provide precision of "
2958 << TOFTimePrecPS << " ps";
2959
2960 // check if there are tracks orphan tracks at all
2961 if (!trackRef.getEntries()) {
2962 return;
2963 }
2964 // the bscMap has at least TF start BC
2965 std::uint64_t maxBC = mStartIR.toLong();
2966 const auto& tofClus = data.getTOFClusters();
2967 for (int src = GIndex::NSources; src--;) {
2968 if (!GIndex::getSourceDetectorsMask(src)[o2::detectors::DetID::TOF]) { // check only tracks with TOF contribution
2969 continue;
2970 }
2971 int start = trackRef.getFirstEntryOfSource(src);
2972 int end = start + trackRef.getEntriesOfSource(src);
2973 for (int ti = start; ti < end; ti++) {
2974 auto& trackIndex = GIndices[ti];
2975 const auto& tofMatch = data.getTOFMatch(trackIndex);
2976 const auto& tofInt = tofMatch.getLTIntegralOut();
2977 float intLen = tofInt.getL();
2978 float tofExpMom = 0.;
2979 if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) {
2980 float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed));
2981 if (expBeta > o2::constants::math::Almost1) {
2983 }
2984 tofExpMom = o2::constants::physics::MassPionCharged * expBeta / std::sqrt(1.f - expBeta * expBeta);
2985 } else {
2986 continue;
2987 }
2988 double massZ = o2::track::PID::getMass2Z(data.getTrackParam(trackIndex).getPID());
2989 double energy = sqrt((massZ * massZ) + (tofExpMom * tofExpMom));
2990 double exp = intLen * energy / (cSpeed * tofExpMom);
2991 auto tofSignal = (tofMatch.getSignal() - exp) * 1e-3; // time in ns wrt TF start
2992 auto bc = relativeTime_to_GlobalBC(tofSignal);
2993
2994 auto it = bcsMap.lower_bound(bc);
2995 if (it == bcsMap.end() || it->first > bc + maxGapBC) {
2996 bcsMap.emplace_hint(it, bc, 1);
2997 LOG(debug) << "adding dummy BC " << bc;
2998 }
2999 if (bc > maxBC) {
3000 maxBC = bc;
3001 }
3002 }
3003 }
3004 // make sure there is a globalBC exceeding the max encountered bc
3005 if ((--bcsMap.end())->first <= maxBC) {
3006 bcsMap.emplace_hint(bcsMap.end(), maxBC + 1, 1);
3007 }
3008 // renumber BCs
3009 int bcID = 0;
3010 for (auto& item : bcsMap) {
3011 item.second = bcID;
3012 bcID++;
3013 }
3014}
3015
3016std::uint64_t AODProducerWorkflowDPL::fillBCSlice(int (&slice)[2], double tmin, double tmax, const std::map<uint64_t, int>& bcsMap) const
3017{
3018 // for ambiguous tracks (no or multiple vertices) we store the BC slice corresponding to track time window used for track-vertex matching,
3019 // see VertexTrackMatcher::extractTracks creator method, i.e. central time estimated +- uncertainty defined as:
3020 // 1) for tracks having a gaussian time error: PVertexerParams.nSigmaTimeTrack * trackSigma + PVertexerParams.timeMarginTrackTime
3021 // 2) for tracks having time uncertainty in a fixed time interval (TPC,ITS,MFT..): half of the interval + PVertexerParams.timeMarginTrackTime
3022 // The track time in the TrackExtraInfo is stored in ns wrt the collision BC for unambigous tracks and wrt bcSlice[0] for ambiguous ones,
3023 // with convention for errors: trackSigma in case (1) and half of the time interval for case (2) above.
3024
3025 // find indices of widest slice of global BCs in the map compatible with provided BC range. bcsMap is guaranteed to be non-empty.
3026 // We also assume that tmax >= tmin.
3027
3028 uint64_t bcMin = relativeTime_to_GlobalBC(tmin), bcMax = relativeTime_to_GlobalBC(tmax);
3029
3030 /*
3031 // brute force way of searching bcs via direct binary search in the map
3032 auto lower = bcsMap.lower_bound(bcMin), upper = bcsMap.upper_bound(bcMax);
3033
3034 if (lower == bcsMap.end()) {
3035 --lower;
3036 }
3037 if (upper != lower) {
3038 --upper;
3039 }
3040 slice[0] = std::distance(bcsMap.begin(), lower);
3041 slice[1] = std::distance(bcsMap.begin(), upper);
3042 */
3043
3044 // faster way to search in bunch crossing via the accelerated bunch crossing lookup structure
3045 auto p = mBCLookup.lower_bound(bcMin);
3046 // assuming that bcMax will be >= bcMin and close to bcMin; we can find
3047 // the upper bound quickly by lineary iterating from p.first to the point where
3048 // the time becomes larger than bcMax.
3049 // (if this is not the case we could determine it with a similar call to mBCLookup)
3050 auto& bcvector = mBCLookup.getBCTimeVector();
3051 auto upperindex = p.first;
3052 while (upperindex < bcvector.size() && bcvector[upperindex] <= bcMax) {
3053 upperindex++;
3054 }
3055 if (upperindex != p.first) {
3056 upperindex--;
3057 }
3058 slice[0] = p.first;
3059 slice[1] = upperindex;
3060
3061 auto bcOfTimeRef = p.second - this->mStartIR.toLong();
3062 LOG(debug) << "BC slice t:" << tmin << " " << slice[0]
3063 << " t: " << tmax << " " << slice[1]
3064 << " bcref: " << bcOfTimeRef;
3065 return bcOfTimeRef;
3066}
3067
3068std::vector<uint8_t> AODProducerWorkflowDPL::fillBCFlags(const o2::globaltracking::RecoContainer& data, std::map<uint64_t, int>& bcsMap) const
3069{
3070 std::vector<uint8_t> flags(bcsMap.size());
3071
3072 // flag BCs belonging to UPC mode ITS ROFs
3073 auto bcIt = bcsMap.begin();
3074 auto itsrofs = data.getITSTracksROFRecords();
3077 for (auto& rof : itsrofs) {
3078 if (!rof.getFlag(o2::itsmft::ROFRecord::VtxUPCMode)) {
3079 continue;
3080 }
3081 uint64_t globalBC0 = rof.getBCData().toLong() + bROF, globalBC1 = globalBC0 + lROF - 1;
3082 // BCs are sorted, iterate until the start of ROF
3083 while (bcIt != bcsMap.end()) {
3084 if (bcIt->first < globalBC0) {
3085 ++bcIt;
3086 continue;
3087 }
3088 if (bcIt->first > globalBC1) {
3089 break;
3090 }
3091 flags[bcIt->second] |= o2::aod::bc::ITSUPCMode;
3092 ++bcIt;
3093 }
3094 }
3095 return flags;
3096}
3097
3099{
3100 LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots",
3101 mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1);
3102
3103 mStreamer.reset();
3104}
3105
3106DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun, bool enableFITextra)
3107{
3108 auto dataRequest = std::make_shared<DataRequest>();
3109 dataRequest->inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", CTPConfigPerRun));
3110
3111 dataRequest->requestTracks(src, useMC);
3112 dataRequest->requestPrimaryVertices(useMC);
3113 if (src[GID::CTP]) {
3114 dataRequest->requestCTPDigits(useMC);
3115 }
3116 if (enableSV) {
3117 dataRequest->requestSecondaryVertices(useMC);
3118 }
3119 if (enableStrangenessTracking) {
3120 dataRequest->requestStrangeTracks(useMC);
3121 LOGF(info, "requestStrangeTracks Finish");
3122 }
3123 if (src[GID::ITS]) {
3124 dataRequest->requestClusters(GIndex::getSourcesMask("ITS"), false);
3125 }
3126 if (src[GID::TPC]) {
3127 dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks
3128 }
3129 if (src[GID::TOF]) {
3130 dataRequest->requestTOFClusters(useMC);
3131 }
3132 if (src[GID::PHS]) {
3133 dataRequest->requestPHOSCells(useMC);
3134 }
3135 if (src[GID::TRD]) {
3136 dataRequest->requestTRDTracklets(false);
3137 }
3138 if (src[GID::EMC]) {
3139 dataRequest->requestEMCALCells(useMC);
3140 }
3141 if (src[GID::CPV]) {
3142 dataRequest->requestCPVClusters(useMC);
3143 }
3144
3145 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(true, // orbitResetTime
3146 true, // GRPECS=true
3147 true, // GRPLHCIF
3148 true, // GRPMagField
3149 true, // askMatLUT
3151 dataRequest->inputs,
3152 true); // query only once all objects except mag.field
3153
3154 dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1));
3155
3156 using namespace o2::aod;
3157 using namespace o2::aodproducer;
3158
3159 std::vector<OutputSpec> outputs{
3193 OutputSpec{"TFN", "TFNumber"},
3194 OutputSpec{"TFF", "TFFilename"},
3195 OutputSpec{"AMD", "AODMetadataKeys"},
3196 OutputSpec{"AMD", "AODMetadataVals"}};
3197
3198 if (useMC) {
3199 outputs.insert(outputs.end(),
3200 {OutputForTable<McCollisions>::spec(),
3201 OutputForTable<HepMCXSections>::spec(),
3202 OutputForTable<HepMCPdfInfos>::spec(),
3203 OutputForTable<HepMCHeavyIons>::spec(),
3204 OutputForTable<McMFTTrackLabels>::spec(),
3205 OutputForTable<McFwdTrackLabels>::spec(),
3206 OutputForTable<StoredMcParticles_001>::spec(),
3207 OutputForTable<McTrackLabels>::spec(),
3208 OutputForTable<McCaloLabels_001>::spec(),
3209 // todo: use addTableToOuput helper?
3210 // currently the description is MCCOLLISLABEL, so
3211 // the name in AO2D would be O2mccollislabel
3212 // addTableToOutput<McCollisionLabels>(outputs);
3213 {OutputLabel{"McCollisionLabels"}, "AOD", "MCCOLLISIONLABEL", 0, Lifetime::Timeframe}});
3214 }
3215
3216 return DataProcessorSpec{
3217 "aod-producer-workflow",
3218 dataRequest->inputs,
3219 outputs,
3220 AlgorithmSpec{adaptFromTask<AODProducerWorkflowDPL>(src, dataRequest, ggRequest, enableSV, useMC, enableFITextra)},
3221 Options{
3222 ConfigParamSpec{"run-number", VariantType::Int64, -1L, {"The run-number. If left default we try to get it from DPL header."}},
3223 ConfigParamSpec{"aod-timeframe-id", VariantType::Int64, -1L, {"Set timeframe number"}},
3224 ConfigParamSpec{"fill-calo-cells", VariantType::Int, 1, {"Fill calo cells into cell table"}},
3225 ConfigParamSpec{"enable-truncation", VariantType::Int, 1, {"Truncation parameter: 1 -- on, != 1 -- off"}},
3226 ConfigParamSpec{"lpmp-prod-tag", VariantType::String, "", {"LPMProductionTag"}},
3227 ConfigParamSpec{"anchor-pass", VariantType::String, "", {"AnchorPassName"}},
3228 ConfigParamSpec{"anchor-prod", VariantType::String, "", {"AnchorProduction"}},
3229 ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}},
3230 ConfigParamSpec{"nthreads", VariantType::Int, std::max(1, int(std::thread::hardware_concurrency() / 2)), {"Number of threads"}},
3231 ConfigParamSpec{"reco-mctracks-only", VariantType::Int, 0, {"Store only reconstructed MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}},
3232 ConfigParamSpec{"ctpreadout-create", VariantType::Int, 0, {"Create CTP digits from detector readout and CTP inputs. !=1 -- off, 1 -- on"}},
3233 ConfigParamSpec{"emc-select-leading", VariantType::Bool, false, {"Flag to select if only the leading contributing particle for an EMCal cell should be stored"}},
3234 ConfigParamSpec{"propagate-tracks", VariantType::Bool, false, {"Propagate tracks (not used for secondary vertices) to IP"}},
3235 ConfigParamSpec{"hepmc-update", VariantType::String, "always", {"When to update HepMC Aux tables: always - force update, never - never update, all - if all keys are present, any - when any key is present (not valid yet)"}},
3236 ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}},
3237 ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}},
3238 ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}},
3239 ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}},
3240 ConfigParamSpec{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}},
3241 ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}},
3242 }};
3243}
3244
3245} // namespace o2::aodproducer
Class to refer to the reconstructed information.
Definition of the 32 Central Trigger System (CTS) Trigger Types defined in https://twiki....
General auxilliary methods.
definition of CTPConfiguration and related CTP structures
Wrapper container for different reconstructed object types.
Definition of the MCH cluster minimal structure.
Reconstructed MID track.
Base track model for the Barrel, params only, w/o covariance.
uint64_t exp(uint64_t base, uint8_t exp) noexcept
definition of CTPDigit, CTPInputDigit
int16_t charge
Definition RawEventData.h:5
uint64_t vertex
Definition RawEventData.h:9
uint64_t bc
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
Definition of the FIT RecPoints class.
Definition of the FV0 RecPoints class.
int32_t i
std::string getType()
Definition Utils.h:138
Header of the AggregatedRunInfo struct.
Global Forward Muon tracks.
Global index for barrel track: provides provenance (detectors combination), index in respective array...
Definition of the MCTrack class.
Definition of a container to keep Monte Carlo truth external to simulation objects.
Utility functions for MC particles.
Definition of the MCH ROFrame record.
Class to perform MFT MCH (and MID) matching.
Class to store the output of the matching to HMPID.
Class to perform TOF matching to global tracks.
Definition of the parameter class for the detector electronics.
Header to collect physics constants.
uint32_t j
Definition RawData.h:0
Definition of the FDD RecPoint class.
Definition of tools for track extrapolation.
Definition of the ITS track.
Definition of the MUON track.
Definition of the MCH track.
Definition of the MCH track parameters for internal use.
Result of refitting TPC-ITS matched track.
std::ostringstream debug
Extention of GlobalTrackID by flags relevant for verter-track association.
Referenc on track indices contributing to the vertex, with possibility chose tracks from specific sou...
Container class to store energy released in the ZDC.
Container class to store a TDC hit in a ZDC channel.
int nClusters
const auto & getBCPattern() const
void GetStartVertex(TVector3 &vertex) const
Definition MCTrack.h:326
void endOfStream(framework::EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
void finaliseCCDB(ConcreteDataMatcher &matcher, void *obj) final
std::pair< size_t, uint64_t > lower_bound(uint64_t timestamp) const
void init(std::map< uint64_t, int > const &bcs)
initialize this container (to be ready for lookup/search queries)
void clear()
clear/reset this container
std::vector< uint64_t > const & getBCTimeVector() const
return the sorted vector of increaing BC times
static float getAmplitude(const o2::emcal::Cell &cell)
static int16_t getLnAmplitude(const o2::emcal::Cell &)
static int8_t getTriggerBits(const o2::emcal::Cell &)
static int16_t getCellNumber(const o2::emcal::Cell &cell)
static int16_t getFastOrAbsID(const o2::emcal::Cell &)
static bool isTRU(const o2::emcal::Cell &cell)
static float getTimeStamp(const o2::emcal::Cell &cell)
void checkUpdates(o2::framework::ProcessingContext &pc)
static GRPGeomHelper & instance()
void setRequest(std::shared_ptr< GRPGeomRequest > req)
static constexpr float MAX_SIN_PHI
Definition Propagator.h:72
static constexpr float MAX_STEP
Definition Propagator.h:73
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:143
void printStream(std::ostream &stream) const
static mask_t getSourcesMask(const std::string_view srcList)
VertexBase getMeanVertex(float z) const
int getEntriesOfSource(int s) const
Definition VtxTrackRef.h:62
int getFirstEntryOfSource(int s) const
Definition VtxTrackRef.h:55
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:58
static constexpr ID ITS
Definition DetID.h:63
static constexpr ID MFT
Definition DetID.h:71
static constexpr ID TPC
Definition DetID.h:64
static constexpr ID EMC
Definition DetID.h:69
static constexpr ID TOF
Definition DetID.h:66
Handler for EMCAL event data.
void reset()
Reset containers with empty ranges.
void setCellData(CellRange cells, TriggerRange triggers)
Setting the data at cell level.
void setCellMCTruthContainer(const o2::dataformats::MCTruthContainer< o2::emcal::MCLabel > *mclabels)
Setting the pointer for the MCTruthContainer for cells.
InteractionRecord getInteractionRecordForEvent(int eventID) const
Get the interaction record for the given event.
int getNumberOfEvents() const
Get the number of events handled by the event handler.
void snapshot(const Output &spec, T const &object)
ConfigParamRegistry const & options()
Definition InitContext.h:33
decltype(auto) get(R binding, int part=0) const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
ServiceRegistryRef services()
The services registry associated with this processing context.
static constexpr int NCellsA
Definition Geometry.h:52
o2::dataformats::GlobalFwdTrack MCHtoFwd(const o2::mch::TrackParam &mchTrack)
Converts mchTrack parameters to Forward coordinate system.
static bool extrapToZ(TrackParam &trackParam, double zEnd)
static void setField()
static bool extrapToVertex(TrackParam &trackParam, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx)
Definition TrackExtrap.h:61
static bool extrapToVertexWithoutBranson(TrackParam &trackParam, double zVtx)
Definition TrackExtrap.h:73
track parameters for internal use
Definition TrackParam.h:34
Double_t getNonBendingCoor() const
return non bending coordinate (cm)
Definition TrackParam.h:51
Double_t getBendingCoor() const
return bending coordinate (cm)
Definition TrackParam.h:59
void setCellMCTruthContainer(const o2::dataformats::MCTruthContainer< o2::phos::MCLabel > *mclabels)
Setting the pointer for the MCTruthContainer for cells.
void setCellData(CellRange cells, TriggerRange triggers)
Setting the data at cell level.
void reset()
Reset containers with empty ranges.
InteractionRecord getInteractionRecordForEvent(int eventID) const
int getNumberOfEvents() const
MCTrack const * getTrack(o2::MCCompLabel const &) const
void releaseTracksForSourceAndEvent(int source, int event)
API to ask releasing tracks (freeing memory) for source + event.
std::vector< MCTrack > const & getTracks(int source, int event) const
variant returning all tracks for source and event at once
static void addInteractionBC(int bc, bool fromCollisonCotext=false)
Definition Utils.cxx:52
static constexpr ID Pion
Definition PID.h:96
Double_t getX() const
Definition TrackFwd.h:49
void set(const std::string &s, int base=2)
Definition EnumFlags.h:401
bool match(const std::vector< std::string > &queries, const char *pattern)
Definition dcs-ccdb.cxx:229
struct _cl_event * event
Definition glcorearb.h:2982
GLfloat GLfloat GLfloat alpha
Definition glcorearb.h:279
GLint GLenum GLint x
Definition glcorearb.h:403
GLenum src
Definition glcorearb.h:1767
GLint GLsizei count
Definition glcorearb.h:399
GLuint entry
Definition glcorearb.h:5735
GLint GLint GLsizei GLuint * counters
Definition glcorearb.h:3985
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLdouble GLdouble right
Definition glcorearb.h:4077
GLdouble f
Definition glcorearb.h:310
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLboolean * data
Definition glcorearb.h:298
GLintptr offset
Definition glcorearb.h:660
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLfloat v0
Definition glcorearb.h:811
GLbitfield flags
Definition glcorearb.h:1570
GLboolean r
Definition glcorearb.h:1233
GLuint start
Definition glcorearb.h:469
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
constexpr std::array< float, 2 > trackQAScaledTOF
Definition DataTypes.h:134
constexpr std::array< float, 5 > trackQAScaleContP1
Definition DataTypes.h:131
uint8_t itsSharedClusterMap uint8_t
constexpr std::array< float, 5 > trackQAScaleContP0
Definition DataTypes.h:130
constexpr std::array< float, 5 > trackQAScaleGloP0
Definition DataTypes.h:132
constexpr std::array< float, 5 > trackQAScaleGloP1
Definition DataTypes.h:133
constexpr float trackQAScaleBins
Definition DataTypes.h:128
constexpr float trackQARefRadius
Definition DataTypes.h:127
bool updateHepMCHeavyIon(const HeavyIonCursor &cursor, int collisionID, short generatorID, o2::dataformats::MCEventHeader const &header, HepMCUpdate when=HepMCUpdate::anyKey)
short updateMCCollisions(const CollisionCursor &cursor, int bcId, float time, o2::dataformats::MCEventHeader const &header, short generatorId=0, int sourceId=0, unsigned int mask=0xFFFFFFF0)
bool updateHepMCPdfInfo(const PdfInfoCursor &cursor, int collisionID, short generatorID, o2::dataformats::MCEventHeader const &header, HepMCUpdate when=HepMCUpdate::anyKey)
uint32_t updateParticles(const ParticleCursor &cursor, int collisionID, std::vector< MCTrack > const &tracks, TrackToIndex &preselect, uint32_t offset=0, bool filter=false, bool background=false, uint32_t weightMask=0xFFFFFFF0, uint32_t momentumMask=0xFFFFFFF0, uint32_t positionMask=0xFFFFFFF0)
bool updateHepMCXSection(const XSectionCursor &cursor, int collisionID, short generatorID, o2::dataformats::MCEventHeader const &header, HepMCUpdate when=HepMCUpdate::anyKey)
void keepMCParticle(std::vector< std::vector< std::unordered_map< int, int > > > &store, int source, int event, int track, int value=1)
framework::DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableST, bool useMC, bool CTPConfigPerRun, bool enableFITextra)
create a processor spec
void dimensionMCKeepStore(std::vector< std::vector< std::unordered_map< int, int > > > &store, int Nsources, int NEvents)
void clearMCKeepStore(std::vector< std::vector< std::unordered_map< int, int > > > &store)
constexpr int LHCMaxBunches
constexpr double LHCBunchSpacingNS
constexpr float Almost1
constexpr double MassPionCharged
Defining PrimaryVertex explicitly as messageable.
Definition TFIDInfo.h:20
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
std::array< T, N > array
int angle2Sector(float phi)
Definition Utils.h:183
float sector2Angle(int sect)
Definition Utils.h:193
TrackParCovF TrackParCov
Definition Track.h:33
constexpr uint32_t Cal
Definition Triggers.h:32
const int TDCSignal[NTDCChannels]
Definition Constants.h:181
constexpr int NTDCChannels
Definition Constants.h:90
constexpr int NChannels
Definition Constants.h:65
std::string fullVersion()
get full version information (official O2 release and git commit)
GPUReconstruction * rec
static OutputSpec const spec()
decltype(FFL(std::declval< cursor_t >())) cursor
gsl::span< const o2::trd::TriggerRecord > getTRDTriggerRecords() const
void collectData(o2::framework::ProcessingContext &pc, const DataRequest &request)
static bool downsampleTsallisCharged(float pt, float factorPt, float sqrts, float &weight, float rnd, float mass=0.13957)
Definition Tsallis.cxx:31
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
Cluster clu
std::uniform_int_distribution< unsigned long long > distr
std::random_device rd
std::array< uint16_t, 5 > pattern