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