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