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::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 sTrkID = 0;
1540 int nV0 = 0;
1541 int nCasc = 0;
1542 int nD3Body = 0;
1543
1544 for (const auto& sTrk : recoData.getStrangeTracks()) {
1545 if (sTrk.mPartType == dataformats::kStrkV0) {
1546 nV0++;
1547 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1548 nCasc++;
1549 } else {
1550 nD3Body++;
1551 }
1552 }
1553
1554 v0Curs.reserve(nV0);
1555 cascCurs.reserve(nCasc);
1556 d3BodyCurs.reserve(nD3Body);
1557
1558 for (const auto& sTrk : recoData.getStrangeTracks()) {
1559 auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS};
1560 auto item = mGIDToTableID.find(ITSIndex);
1561 if (item != mGIDToTableID.end()) {
1562 itsTableIdx = item->second;
1563 } else {
1564 LOG(warn) << "Could not find a ITS strange track index " << ITSIndex;
1565 continue;
1566 }
1567 if (sTrk.mPartType == dataformats::kStrkV0) {
1568 v0Curs(mStrTrkIndices[sTrkID++],
1569 itsTableIdx,
1570 sTrk.mDecayRef,
1571 sTrk.mDecayVtx[0],
1572 sTrk.mDecayVtx[1],
1573 sTrk.mDecayVtx[2],
1574 sTrk.mMasses[0],
1575 sTrk.mMasses[1],
1576 sTrk.mMatchChi2,
1577 sTrk.mTopoChi2,
1578 sTrk.getAverageClusterSize());
1579 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1580 cascCurs(mStrTrkIndices[sTrkID++],
1581 itsTableIdx,
1582 sTrk.mDecayRef,
1583 sTrk.mDecayVtx[0],
1584 sTrk.mDecayVtx[1],
1585 sTrk.mDecayVtx[2],
1586 sTrk.mMasses[0],
1587 sTrk.mMasses[1],
1588 sTrk.mMatchChi2,
1589 sTrk.mTopoChi2,
1590 sTrk.getAverageClusterSize());
1591 } else {
1592 d3BodyCurs(mStrTrkIndices[sTrkID++],
1593 itsTableIdx,
1594 sTrk.mDecayRef,
1595 sTrk.mDecayVtx[0],
1596 sTrk.mDecayVtx[1],
1597 sTrk.mDecayVtx[2],
1598 sTrk.mMasses[0],
1599 sTrk.mMasses[1],
1600 sTrk.mMatchChi2,
1601 sTrk.mTopoChi2,
1602 sTrk.getAverageClusterSize());
1603 }
1604 }
1605}
1606
1607void AODProducerWorkflowDPL::countTPCClusters(const o2::globaltracking::RecoContainer& data)
1608{
1609 const auto& tpcTracks = data.getTPCTracks();
1610 const auto& tpcClusRefs = data.getTPCTracksClusterRefs();
1611 const auto& tpcClusShMap = data.clusterShMapTPC;
1612 const auto& tpcClusAcc = data.getTPCClusters();
1613 constexpr int maxRows = 152;
1614 constexpr int neighbour = 2;
1615 int ntr = tpcTracks.size();
1616 mTPCCounters.clear();
1617 mTPCCounters.resize(ntr);
1618#ifdef WITH_OPENMP
1619 int ngroup = std::min(50, std::max(1, ntr / mNThreads));
1620#pragma omp parallel for schedule(dynamic, ngroup) num_threads(mNThreads)
1621#endif
1622 for (int itr = 0; itr < ntr; itr++) {
1623 std::array<bool, maxRows> clMap{}, shMap{};
1624 uint8_t sectorIndex, rowIndex;
1625 uint32_t clusterIndex;
1626 auto& counters = mTPCCounters[itr];
1627 const auto& track = tpcTracks[itr];
1628 for (int i = 0; i < track.getNClusterReferences(); i++) {
1629 o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef());
1630 unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex;
1631 clMap[rowIndex] = true;
1632 if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) {
1633 if (!shMap[rowIndex]) {
1634 counters.shared++;
1635 }
1636 shMap[rowIndex] = true;
1637 }
1638 }
1639 int last = -1;
1640 for (int i = 0; i < maxRows; i++) {
1641 if (clMap[i]) {
1642 counters.crossed++;
1643 counters.found++;
1644 last = i;
1645 } else if ((i - last) <= neighbour) {
1646 counters.crossed++;
1647 } else {
1648 int lim = std::min(i + 1 + neighbour, maxRows);
1649 for (int j = i + 1; j < lim; j++) {
1650 if (clMap[j]) {
1651 counters.crossed++;
1652 break;
1653 }
1654 }
1655 }
1656 }
1657 }
1658}
1659
1660uint8_t AODProducerWorkflowDPL::getTRDPattern(const o2::trd::TrackTRD& track)
1661{
1662 uint8_t pattern = 0;
1663 for (int il = o2::trd::TrackTRD::EGPUTRDTrack::kNLayers - 1; il >= 0; il--) {
1664 if (track.getTrackletIndex(il) != -1) {
1665 pattern |= 0x1 << il;
1666 }
1667 }
1668 if (track.getHasNeighbor()) {
1669 pattern |= 0x1 << 6;
1670 }
1671 if (track.getHasPadrowCrossing()) {
1672 pattern |= 0x1 << 7;
1673 }
1674 return pattern;
1675}
1676
1677template <typename TCaloHandler, typename TCaloCursor, typename TCaloTRGCursor, typename TMCCaloLabelCursor>
1678void AODProducerWorkflowDPL::addToCaloTable(TCaloHandler& caloHandler, TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor,
1679 TMCCaloLabelCursor& mcCaloCellLabelCursor, int eventID, int bcID, int8_t caloType)
1680{
1681 auto inputEvent = caloHandler.buildEvent(eventID);
1682 auto cellsInEvent = inputEvent.mCells; // get cells belonging to current event
1683 auto cellMClabels = inputEvent.mMCCellLabels; // get MC labels belonging to current event (only implemented for EMCal currently!)
1684 caloCellCursor.reserve(cellsInEvent.size() + caloCellCursor.lastIndex() + 1);
1685 caloTRGCursor.reserve(cellsInEvent.size() + caloTRGCursor.lastIndex() + 1);
1686 if (mUseMC) {
1687 mcCaloCellLabelCursor.reserve(cellsInEvent.size() + mcCaloCellLabelCursor.lastIndex() + 1);
1688 }
1689 for (auto iCell = 0U; iCell < cellsInEvent.size(); iCell++) {
1690 caloCellCursor(bcID,
1691 CellHelper::getCellNumber(cellsInEvent[iCell]),
1692 truncateFloatFraction(CellHelper::getAmplitude(cellsInEvent[iCell]), mCaloAmp),
1693 truncateFloatFraction(CellHelper::getTimeStamp(cellsInEvent[iCell]), mCaloTime),
1694 cellsInEvent[iCell].getType(),
1695 caloType); // 1 = emcal, -1 = undefined, 0 = phos
1696
1697 // todo: fix dummy values in CellHelper when it is clear what is filled for trigger information
1698 if (CellHelper::isTRU(cellsInEvent[iCell])) { // Write only trigger cells into this table
1699 caloTRGCursor(bcID,
1700 CellHelper::getFastOrAbsID(cellsInEvent[iCell]),
1701 CellHelper::getLnAmplitude(cellsInEvent[iCell]),
1702 CellHelper::getTriggerBits(cellsInEvent[iCell]),
1703 caloType);
1704 }
1705 if (mUseMC) {
1706 // Common for PHOS and EMCAL
1707 // loop over all MC Labels for the current cell
1708 std::vector<int32_t> particleIds;
1709 std::vector<float> amplitudeFraction;
1710 if (!mEMCselectLeading) {
1711 particleIds.reserve(cellMClabels.size());
1712 amplitudeFraction.reserve(cellMClabels.size());
1713 }
1714 float tmpMaxAmplitude = 0;
1715 int32_t tmpindex = 0;
1716 for (auto& mclabel : cellMClabels[iCell]) {
1717 // do not fill noise lables!
1718 if (mclabel.isValid()) {
1719 if (mEMCselectLeading) {
1720 if (mclabel.getAmplitudeFraction() > tmpMaxAmplitude) {
1721 // Check if this MCparticle added to be kept?
1722 if (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).find(mclabel.getTrackID()) !=
1723 mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).end()) {
1724 tmpMaxAmplitude = mclabel.getAmplitudeFraction();
1725 tmpindex = (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID())).at(mclabel.getTrackID());
1726 }
1727 }
1728 } else {
1729 auto trackStore = mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID());
1730 auto iter = trackStore.find(mclabel.getTrackID());
1731 if (iter != trackStore.end()) {
1732 amplitudeFraction.emplace_back(mclabel.getAmplitudeFraction());
1733 particleIds.emplace_back(iter->second);
1734 } else {
1735 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!
1736 amplitudeFraction.emplace_back(0.f);
1737 LOG(warn) << "CaloTable: Could not find track for mclabel (" << mclabel.getSourceID() << "," << mclabel.getEventID() << "," << mclabel.getTrackID() << ") in the AOD MC store";
1738 if (mMCKineReader) {
1739 auto mctrack = mMCKineReader->getTrack(mclabel);
1740 TVector3 vec;
1741 mctrack->GetStartVertex(vec);
1742 LOG(warn) << " ... this track is of PDG " << mctrack->GetPdgCode() << " produced by " << mctrack->getProdProcessAsString() << " at (" << vec.X() << "," << vec.Y() << "," << vec.Z() << ")";
1743 }
1744 }
1745 }
1746 }
1747 } // end of loop over all MC Labels for the current cell
1748 if (mEMCselectLeading) {
1749 amplitudeFraction.emplace_back(tmpMaxAmplitude);
1750 particleIds.emplace_back(tmpindex);
1751 }
1752 if (particleIds.size() == 0) {
1753 particleIds.emplace_back(-1);
1754 amplitudeFraction.emplace_back(0.f);
1755 }
1756 mcCaloCellLabelCursor(particleIds,
1757 amplitudeFraction);
1758 }
1759 } // end of loop over cells in current event
1760}
1761
1762// fill calo related tables (cells and calotrigger table)
1763template <typename TCaloCursor, typename TCaloTRGCursor, typename TMCCaloLabelCursor>
1764void AODProducerWorkflowDPL::fillCaloTable(TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor,
1765 TMCCaloLabelCursor& mcCaloCellLabelCursor, const std::map<uint64_t, int>& bcsMap,
1767{
1768 // get calo information
1769 auto caloEMCCells = data.getEMCALCells();
1770 auto caloEMCCellsTRGR = data.getEMCALTriggers();
1771 auto mcCaloEMCCellLabels = data.getEMCALCellsMCLabels();
1772
1773 auto caloPHOSCells = data.getPHOSCells();
1774 auto caloPHOSCellsTRGR = data.getPHOSTriggers();
1775 auto mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels();
1776
1777 if (!mInputSources[GIndex::PHS]) {
1778 caloPHOSCells = {};
1779 caloPHOSCellsTRGR = {};
1780 mcCaloPHOSCellLabels = {};
1781 }
1782
1783 if (!mInputSources[GIndex::EMC]) {
1784 caloEMCCells = {};
1785 caloEMCCellsTRGR = {};
1786 mcCaloEMCCellLabels = {};
1787 }
1788
1791
1792 // get cell belonging to an eveffillnt instead of timeframe
1793 emcEventHandler.reset();
1794 emcEventHandler.setCellData(caloEMCCells, caloEMCCellsTRGR);
1795 emcEventHandler.setCellMCTruthContainer(mcCaloEMCCellLabels);
1796
1797 phsEventHandler.reset();
1798 phsEventHandler.setCellData(caloPHOSCells, caloPHOSCellsTRGR);
1799 phsEventHandler.setCellMCTruthContainer(mcCaloPHOSCellLabels);
1800
1801 int emcNEvents = emcEventHandler.getNumberOfEvents();
1802 int phsNEvents = phsEventHandler.getNumberOfEvents();
1803
1804 std::vector<std::tuple<uint64_t, int8_t, int>> caloEvents; // <bc, caloType, eventID>
1805
1806 caloEvents.reserve(emcNEvents + phsNEvents);
1807
1808 for (int iev = 0; iev < emcNEvents; ++iev) {
1809 uint64_t bc = emcEventHandler.getInteractionRecordForEvent(iev).toLong();
1810 caloEvents.emplace_back(std::make_tuple(bc, 1, iev));
1811 }
1812
1813 for (int iev = 0; iev < phsNEvents; ++iev) {
1814 uint64_t bc = phsEventHandler.getInteractionRecordForEvent(iev).toLong();
1815 caloEvents.emplace_back(std::make_tuple(bc, 0, iev));
1816 }
1817
1818 std::sort(caloEvents.begin(), caloEvents.end(),
1819 [](const auto& left, const auto& right) { return std::get<0>(left) < std::get<0>(right); });
1820
1821 // loop over events
1822 for (int i = 0; i < emcNEvents + phsNEvents; ++i) {
1823 uint64_t globalBC = std::get<0>(caloEvents[i]);
1824 int8_t caloType = std::get<1>(caloEvents[i]);
1825 int eventID = std::get<2>(caloEvents[i]);
1826 auto item = bcsMap.find(globalBC);
1827 int bcID = -1;
1828 if (item != bcsMap.end()) {
1829 bcID = item->second;
1830 } else {
1831 LOG(warn) << "Error: could not find a corresponding BC ID for a calo point; globalBC = " << globalBC << ", caloType = " << (int)caloType;
1832 }
1833 if (caloType == 0) { // phos
1834 addToCaloTable(phsEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType);
1835 }
1836 if (caloType == 1) { // emc
1837 addToCaloTable(emcEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType);
1838 }
1839 }
1840
1841 caloEvents.clear();
1842}
1843
1845{
1846 mTimer.Stop();
1848 mLPMProdTag = ic.options().get<std::string>("lpmp-prod-tag");
1849 mAnchorPass = ic.options().get<std::string>("anchor-pass");
1850 mAnchorProd = ic.options().get<std::string>("anchor-prod");
1851 mUser = ic.options().get<std::string>("created-by");
1852 mRecoPass = ic.options().get<std::string>("reco-pass");
1853 mAODParent = ic.options().get<std::string>("aod-parent");
1854 mTFNumber = ic.options().get<int64_t>("aod-timeframe-id");
1855 mRecoOnly = ic.options().get<int>("reco-mctracks-only");
1856 mTruncate = ic.options().get<int>("enable-truncation");
1857 mRunNumber = ic.options().get<int>("run-number");
1858 mCTPReadout = ic.options().get<int>("ctpreadout-create");
1859 mNThreads = std::max(1, ic.options().get<int>("nthreads"));
1860 mEMCselectLeading = ic.options().get<bool>("emc-select-leading");
1861 mThinTracks = ic.options().get<bool>("thin-tracks");
1862 mPropTracks = ic.options().get<bool>("propagate-tracks");
1863 mMaxPropXiu = ic.options().get<float>("propagate-tracks-max-xiu");
1864 mPropMuons = ic.options().get<bool>("propagate-muons");
1865 mStoreAllMFTCov = ic.options().get<bool>("store-all-mft-cov");
1866 if (auto s = ic.options().get<std::string>("with-streamers"); !s.empty()) {
1867 mStreamerFlags.set(s);
1868 if (mStreamerFlags) {
1869 LOGP(info, "Writing streamer data with mask:");
1870 LOG(info) << mStreamerFlags;
1871 } else {
1872 LOGP(warn, "Specified non-default empty streamer mask!");
1873 }
1874 }
1875 mTrackQCKeepGlobalTracks = ic.options().get<bool>("trackqc-keepglobaltracks");
1876 mTrackQCRetainOnlydEdx = ic.options().get<bool>("trackqc-retainonlydedx");
1877 mTrackQCFraction = ic.options().get<float>("trackqc-fraction");
1878 mTrackQCNTrCut = ic.options().get<int64_t>("trackqc-NTrCut");
1879 mTrackQCDCAxy = ic.options().get<float>("trackqc-tpc-dca");
1880 mTrackQCPt = ic.options().get<float>("trackqc-tpc-pt");
1881 mTrackQCNCls = ic.options().get<int>("trackqc-tpc-cls");
1882 if (auto seed = ic.options().get<int>("seed"); seed == 0) {
1883 LOGP(info, "Using random device for seeding");
1884 std::random_device rd;
1885 std::array<int, std::mt19937::state_size> seed_data{};
1886 std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
1887 std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
1888 mGenerator = std::mt19937(seq);
1889 } else {
1890 LOGP(info, "Using seed {} for sampling", seed);
1891 mGenerator.seed(seed);
1892 }
1893#ifdef WITH_OPENMP
1894 LOGP(info, "Multi-threaded parts will run with {} OpenMP threads", mNThreads);
1895#else
1896 mNThreads = 1;
1897 LOG(info) << "OpenMP is disabled";
1898#endif
1899 if (mTFNumber == -1L) {
1900 LOG(info) << "TFNumber will be obtained from CCDB";
1901 }
1902 if (mRunNumber == -1L) {
1903 LOG(info) << "The Run number will be obtained from DPL headers";
1904 }
1905
1906 mUseSigFiltMC = ic.options().get<bool>("mc-signal-filt");
1907
1908 mCollectConfigFiles = ic.options().get<bool>("collect-config-files");
1909
1910 // set no truncation if selected by user
1911 if (mTruncate != 1) {
1912 LOG(info) << "Truncation is not used!";
1913 mCollisionPosition = 0xFFFFFFFF;
1914 mCollisionPositionCov = 0xFFFFFFFF;
1915 mTrackX = 0xFFFFFFFF;
1916 mTrackAlpha = 0xFFFFFFFF;
1917 mTrackSnp = 0xFFFFFFFF;
1918 mTrackTgl = 0xFFFFFFFF;
1919 mTrack1Pt = 0xFFFFFFFF;
1920 mTrackChi2 = 0xFFFFFFFF;
1921 mTrackCovDiag = 0xFFFFFFFF;
1922 mTrackCovOffDiag = 0xFFFFFFFF;
1923 mTrackSignal = 0xFFFFFFFF;
1924 mTrackTime = 0xFFFFFFFF;
1925 mTPCTime0 = 0xFFFFFFFF;
1926 mTrackTimeError = 0xFFFFFFFF;
1927 mTrackPosEMCAL = 0xFFFFFFFF;
1928 mTracklets = 0xFFFFFFFF;
1929 mMcParticleW = 0xFFFFFFFF;
1930 mMcParticlePos = 0xFFFFFFFF;
1931 mMcParticleMom = 0xFFFFFFFF;
1932 mCaloAmp = 0xFFFFFFFF;
1933 mCaloTime = 0xFFFFFFFF;
1934 mCPVPos = 0xFFFFFFFF;
1935 mCPVAmpl = 0xFFFFFFFF;
1936 mMuonTr1P = 0xFFFFFFFF;
1937 mMuonTrThetaX = 0xFFFFFFFF;
1938 mMuonTrThetaY = 0xFFFFFFFF;
1939 mMuonTrZmu = 0xFFFFFFFF;
1940 mMuonTrBend = 0xFFFFFFFF;
1941 mMuonTrNonBend = 0xFFFFFFFF;
1942 mMuonTrCov = 0xFFFFFFFF;
1943 mMuonCl = 0xFFFFFFFF;
1944 mMuonClErr = 0xFFFFFFFF;
1945 mV0Time = 0xFFFFFFFF;
1946 mV0ChannelTime = 0xFFFFFFFF;
1947 mFDDTime = 0xFFFFFFFF;
1948 mFDDChannelTime = 0xFFFFFFFF;
1949 mT0Time = 0xFFFFFFFF;
1950 mT0ChannelTime = 0xFFFFFFFF;
1951 mV0Amplitude = 0xFFFFFFFF;
1952 mFDDAmplitude = 0xFFFFFFFF;
1953 mT0Amplitude = 0xFFFFFFFF;
1954 }
1955 // Initialize ZDC helper maps
1956 for (int ic = 0; ic < o2::zdc::NChannels; ic++) {
1957 mZDCEnergyMap[ic] = -std::numeric_limits<float>::infinity();
1958 }
1959 for (int ic = 0; ic < o2::zdc::NTDCChannels; ic++) {
1960 mZDCTDCMap[ic] = -std::numeric_limits<float>::infinity();
1961 }
1962
1963 std::string hepmcUpdate = ic.options().get<std::string>("hepmc-update");
1964 HepMCUpdate when = (hepmcUpdate == "never" ? HepMCUpdate::never : hepmcUpdate == "always" ? HepMCUpdate::always
1965 : hepmcUpdate == "all" ? HepMCUpdate::allKeys
1966 : HepMCUpdate::anyKey);
1967 mXSectionUpdate = when;
1968 mPdfInfoUpdate = when;
1969 mHeavyIonUpdate = when;
1970
1971 mTimer.Reset();
1972
1973 if (mStreamerFlags) {
1974 mStreamer = std::make_unique<o2::utils::TreeStreamRedirector>("AO2DStreamer.root", "RECREATE");
1975 }
1976}
1977
1978namespace
1979{
1980void add_additional_meta_info(std::vector<TString>& keys, std::vector<TString>& values)
1981{
1982 // see if we should put additional meta info (e.g. from MC)
1983 auto aod_external_meta_info_file = getenv("AOD_ADDITIONAL_METADATA_FILE");
1984 if (aod_external_meta_info_file != nullptr) {
1985 LOG(info) << "Trying to inject additional AOD meta-data from " << aod_external_meta_info_file;
1986 if (std::filesystem::exists(aod_external_meta_info_file)) {
1987 std::ifstream input_file(aod_external_meta_info_file);
1988 if (input_file) {
1989 nlohmann::json json_data;
1990 try {
1991 input_file >> json_data;
1992 } catch (nlohmann::json::parse_error& e) {
1993 std::cerr << "JSON Parse Error: " << e.what() << "\n";
1994 std::cerr << "Exception ID: " << e.id << "\n";
1995 std::cerr << "Byte position: " << e.byte << "\n";
1996 return;
1997 }
1998 // If parsing succeeds, iterate over key-value pairs
1999 for (const auto& [key, value] : json_data.items()) {
2000 LOG(info) << "Adding AOD MetaData" << key << " : " << value;
2001 keys.push_back(key.c_str());
2002 values.push_back(value.get<std::string>());
2003 }
2004 }
2005 }
2006 }
2007}
2008} // namespace
2009
2011{
2012 mTimer.Start(false);
2014 recoData.collectData(pc, *mDataRequest);
2015 updateTimeDependentParams(pc); // Make sure that this is called after the RecoContainer collect data, since some condition objects are fetched there
2016
2017 mStartIR = recoData.startIR;
2018
2019 auto primVertices = recoData.getPrimaryVertices();
2020 auto primVer2TRefs = recoData.getPrimaryVertexMatchedTrackRefs();
2021 auto primVerGIs = recoData.getPrimaryVertexMatchedTracks();
2022 auto primVerLabels = recoData.getPrimaryVertexMCLabels();
2023
2024 auto fddChData = recoData.getFDDChannelsData();
2025 auto fddRecPoints = recoData.getFDDRecPoints();
2026 auto ft0ChData = recoData.getFT0ChannelsData();
2027 auto ft0RecPoints = recoData.getFT0RecPoints();
2028 auto fv0ChData = recoData.getFV0ChannelsData();
2029 auto fv0RecPoints = recoData.getFV0RecPoints();
2030
2031 auto zdcEnergies = recoData.getZDCEnergy();
2032 auto zdcBCRecData = recoData.getZDCBCRecData();
2033 auto zdcTDCData = recoData.getZDCTDCData();
2034
2035 auto cpvClusters = recoData.getCPVClusters();
2036 auto cpvTrigRecs = recoData.getCPVTriggers();
2037
2038 auto ctpDigits = recoData.getCTPDigits();
2039 const auto& tinfo = pc.services().get<o2::framework::TimingInfo>();
2040 std::vector<o2::ctp::CTPDigit> ctpDigitsCreated;
2041 if (mCTPReadout == 1) {
2042 LOG(info) << "CTP : creating ctpreadout in AOD producer";
2043 createCTPReadout(recoData, ctpDigitsCreated, pc);
2044 LOG(info) << "CTP : ctpreadout created from AOD";
2045 ctpDigits = gsl::span<o2::ctp::CTPDigit>(ctpDigitsCreated);
2046 }
2047 LOG(debug) << "FOUND " << primVertices.size() << " primary vertices";
2048 LOG(debug) << "FOUND " << ft0RecPoints.size() << " FT0 rec. points";
2049 LOG(debug) << "FOUND " << fv0RecPoints.size() << " FV0 rec. points";
2050 LOG(debug) << "FOUND " << fddRecPoints.size() << " FDD rec. points";
2051 LOG(debug) << "FOUND " << cpvClusters.size() << " CPV clusters";
2052 LOG(debug) << "FOUND " << cpvTrigRecs.size() << " CPV trigger records";
2053
2054 LOG(info) << "FOUND " << primVertices.size() << " primary vertices";
2055
2056 using namespace o2::aodhelpers;
2057
2058 auto bcCursor = createTableCursor<o2::aod::BCs>(pc);
2059 auto bcFlagsCursor = createTableCursor<o2::aod::BCFlags>(pc);
2060 auto cascadesCursor = createTableCursor<o2::aod::Cascades>(pc);
2061 auto collisionsCursor = createTableCursor<o2::aod::Collisions>(pc);
2062 auto decay3BodyCursor = createTableCursor<o2::aod::Decay3Bodys>(pc);
2063 auto trackedCascadeCursor = createTableCursor<o2::aod::TrackedCascades>(pc);
2064 auto trackedV0Cursor = createTableCursor<o2::aod::TrackedV0s>(pc);
2065 auto tracked3BodyCurs = createTableCursor<o2::aod::Tracked3Bodys>(pc);
2066 auto fddCursor = createTableCursor<o2::aod::FDDs>(pc);
2067 auto fddExtraCursor = createTableCursor<o2::aod::FDDsExtra>(pc);
2068 auto ft0Cursor = createTableCursor<o2::aod::FT0s>(pc);
2069 auto ft0ExtraCursor = createTableCursor<o2::aod::FT0sExtra>(pc);
2070 auto fv0aCursor = createTableCursor<o2::aod::FV0As>(pc);
2071 auto fv0aExtraCursor = createTableCursor<o2::aod::FV0AsExtra>(pc);
2072 auto fwdTracksCursor = createTableCursor<o2::aod::StoredFwdTracks>(pc);
2073 auto fwdTracksCovCursor = createTableCursor<o2::aod::StoredFwdTracksCov>(pc);
2074 auto fwdTrkClsCursor = createTableCursor<o2::aod::FwdTrkCls>(pc);
2075 auto mftTracksCursor = createTableCursor<o2::aod::StoredMFTTracks>(pc);
2076 auto mftTracksCovCursor = createTableCursor<o2::aod::StoredMFTTracksCov>(pc);
2077 auto tracksCursor = createTableCursor<o2::aod::StoredTracksIU>(pc);
2078 auto tracksCovCursor = createTableCursor<o2::aod::StoredTracksCovIU>(pc);
2079 auto tracksExtraCursor = createTableCursor<o2::aod::StoredTracksExtra>(pc);
2080 auto tracksQACursor = createTableCursor<o2::aod::TracksQAVersion>(pc);
2081 auto ambigTracksCursor = createTableCursor<o2::aod::AmbiguousTracks>(pc);
2082 auto ambigMFTTracksCursor = createTableCursor<o2::aod::AmbiguousMFTTracks>(pc);
2083 auto ambigFwdTracksCursor = createTableCursor<o2::aod::AmbiguousFwdTracks>(pc);
2084 auto v0sCursor = createTableCursor<o2::aod::V0s>(pc);
2085 auto zdcCursor = createTableCursor<o2::aod::Zdcs>(pc);
2086 auto hmpCursor = createTableCursor<o2::aod::HMPIDs>(pc);
2087 auto caloCellsCursor = createTableCursor<o2::aod::Calos>(pc);
2088 auto caloCellsTRGTableCursor = createTableCursor<o2::aod::CaloTriggers>(pc);
2089 auto cpvClustersCursor = createTableCursor<o2::aod::CPVClusters>(pc);
2090 auto originCursor = createTableCursor<o2::aod::Origins>(pc);
2091
2094 if (mEnableTRDextra) {
2095 trdExtraCursor = createTableCursor<o2::aod::TRDsExtra>(pc);
2096 }
2097
2098 // Declare MC cursors type without adding the output for a table
2109 if (mUseMC) { // This creates the actual writercursor
2110 mcColLabelsCursor = createTableCursor<o2::aod::McCollisionLabels>(pc);
2111 mcCollisionsCursor = createTableCursor<o2::aod::McCollisions>(pc);
2112 hepmcXSectionsCursor = createTableCursor<o2::aod::HepMCXSections>(pc);
2113 hepmcPdfInfosCursor = createTableCursor<o2::aod::HepMCPdfInfos>(pc);
2114 hepmcHeavyIonsCursor = createTableCursor<o2::aod::HepMCHeavyIons>(pc);
2115 mcMFTTrackLabelCursor = createTableCursor<o2::aod::McMFTTrackLabels>(pc);
2116 mcFwdTrackLabelCursor = createTableCursor<o2::aod::McFwdTrackLabels>(pc);
2117 mcParticlesCursor = createTableCursor<o2::aod::StoredMcParticles_001>(pc);
2118 mcTrackLabelCursor = createTableCursor<o2::aod::McTrackLabels>(pc);
2119 mcCaloLabelsCursor = createTableCursor<o2::aod::McCaloLabels_001>(pc);
2120 }
2121
2122 std::unique_ptr<o2::steer::MCKinematicsReader> mcReader;
2123 if (mUseMC) {
2124 mcReader = std::make_unique<o2::steer::MCKinematicsReader>("collisioncontext.root");
2125 }
2126 mMCKineReader = mcReader.get(); // for use in different functions
2127 std::map<uint64_t, int> bcsMap;
2128 collectBCs(recoData, mUseMC ? mcReader->getDigitizationContext()->getEventRecords() : std::vector<o2::InteractionTimeRecord>{}, bcsMap);
2129 if (!primVer2TRefs.empty()) { // if the vertexing was done, the last slot refers to orphan tracks
2130 addRefGlobalBCsForTOF(primVer2TRefs.back(), primVerGIs, recoData, bcsMap);
2131 }
2132 // initialize the bunch crossing container for further use below
2133 mBCLookup.init(bcsMap);
2134
2135 uint64_t tfNumber;
2136 const int runNumber = (mRunNumber == -1) ? int(tinfo.runNumber) : mRunNumber;
2137 if (mTFNumber == -1L) {
2138 // TODO has to use absolute time of TF
2139 tfNumber = uint64_t(tinfo.firstTForbit) + (uint64_t(tinfo.runNumber) << 32); // getTFNumber(mStartIR, runNumber);
2140 } else {
2141 tfNumber = mTFNumber;
2142 }
2143
2144 std::vector<float> aAmplitudes, aTimes;
2145 std::vector<uint8_t> aChannels;
2146 fv0aCursor.reserve(fv0RecPoints.size());
2147 for (auto& fv0RecPoint : fv0RecPoints) {
2148 aAmplitudes.clear();
2149 aChannels.clear();
2150 aTimes.clear();
2151 const auto channelData = fv0RecPoint.getBunchChannelData(fv0ChData);
2152 for (auto& channel : channelData) {
2153 if (channel.charge > 0) {
2154 aAmplitudes.push_back(truncateFloatFraction(channel.charge, mV0Amplitude));
2155 aTimes.push_back(truncateFloatFraction(channel.time * 1.E-3, mV0ChannelTime));
2156 aChannels.push_back(channel.channel);
2157 }
2158 }
2159 uint64_t bc = fv0RecPoint.getInteractionRecord().toLong();
2160 auto item = bcsMap.find(bc);
2161 int bcID = -1;
2162 if (item != bcsMap.end()) {
2163 bcID = item->second;
2164 } else {
2165 LOG(fatal) << "Error: could not find a corresponding BC ID for a FV0 rec. point; BC = " << bc;
2166 }
2167 fv0aCursor(bcID,
2168 aAmplitudes,
2169 aChannels,
2170 truncateFloatFraction(fv0RecPoint.getCollisionGlobalMeanTime() * 1E-3, mV0Time), // ps to ns
2171 fv0RecPoint.getTrigger().getTriggersignals());
2172
2173 if (mEnableFITextra) {
2174 fv0aExtraCursor(bcID,
2175 aTimes);
2176 }
2177 }
2178
2179 std::vector<float> zdcEnergy, zdcAmplitudes, zdcTime;
2180 std::vector<uint8_t> zdcChannelsE, zdcChannelsT;
2181 zdcCursor.reserve(zdcBCRecData.size());
2182 for (auto zdcRecData : zdcBCRecData) {
2183 uint64_t bc = zdcRecData.ir.toLong();
2184 auto item = bcsMap.find(bc);
2185 int bcID = -1;
2186 if (item != bcsMap.end()) {
2187 bcID = item->second;
2188 } else {
2189 LOG(fatal) << "Error: could not find a corresponding BC ID for a ZDC rec. point; BC = " << bc;
2190 }
2191 int fe, ne, ft, nt, fi, ni;
2192 zdcRecData.getRef(fe, ne, ft, nt, fi, ni);
2193 zdcEnergy.clear();
2194 zdcChannelsE.clear();
2195 zdcAmplitudes.clear();
2196 zdcTime.clear();
2197 zdcChannelsT.clear();
2198 for (int ie = 0; ie < ne; ie++) {
2199 auto& zdcEnergyData = zdcEnergies[fe + ie];
2200 zdcEnergy.emplace_back(zdcEnergyData.energy());
2201 zdcChannelsE.emplace_back(zdcEnergyData.ch());
2202 }
2203 for (int it = 0; it < nt; it++) {
2204 auto& tdc = zdcTDCData[ft + it];
2205 zdcAmplitudes.emplace_back(tdc.amplitude());
2206 zdcTime.emplace_back(tdc.value());
2207 zdcChannelsT.emplace_back(o2::zdc::TDCSignal[tdc.ch()]);
2208 }
2209 zdcCursor(bcID,
2210 zdcEnergy,
2211 zdcChannelsE,
2212 zdcAmplitudes,
2213 zdcTime,
2214 zdcChannelsT);
2215 }
2216
2217 // keep track of event_id + source_id + bc for each mc-collision
2218 std::vector<MCColInfo> mcColToEvSrc;
2219
2220 if (mUseMC) {
2221 using namespace o2::aodmchelpers;
2222
2223 // filling mcCollision table
2224 int nMCCollisions = mcReader->getDigitizationContext()->getNCollisions();
2225 const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords();
2226 const auto& mcParts = mcReader->getDigitizationContext()->getEventParts();
2227
2228 // if signal filtering enabled, let's check if there are more than one source; otherwise fatalise
2229 if (mUseSigFiltMC) {
2230 std::vector<int> sourceIDs{};
2231 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
2232 for (auto const& colPart : mcParts[iCol]) {
2233 int sourceID = colPart.sourceID;
2234 if (std::find(sourceIDs.begin(), sourceIDs.end(), sourceID) == sourceIDs.end()) {
2235 sourceIDs.push_back(sourceID);
2236 }
2237 if (sourceIDs.size() > 1) { // we found more than one, exit
2238 break;
2239 }
2240 }
2241 if (sourceIDs.size() > 1) { // we found more than one, exit
2242 break;
2243 }
2244 }
2245 if (sourceIDs.size() <= 1) {
2246 LOGP(fatal, "Signal filtering cannot be enabled without embedding. Please fix the configuration either enabling the embedding, or turning off the signal filtering.");
2247 }
2248 }
2249
2250 // count all parts
2251 int totalNParts = 0;
2252 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
2253 totalNParts += mcParts[iCol].size();
2254 }
2255 mcCollisionsCursor.reserve(totalNParts);
2256
2257 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
2258 const auto time = mcRecords[iCol].getTimeOffsetWrtBC();
2259 auto globalBC = mcRecords[iCol].toLong();
2260 auto item = bcsMap.find(globalBC);
2261 int bcID = -1;
2262 if (item != bcsMap.end()) {
2263 bcID = item->second;
2264 } else {
2265 LOG(fatal) << "Error: could not find a corresponding BC ID "
2266 << "for MC collision; BC = " << globalBC
2267 << ", mc collision = " << iCol;
2268 }
2269 auto& colParts = mcParts[iCol];
2270 auto nParts = colParts.size();
2271 for (auto colPart : colParts) {
2272 auto eventID = colPart.entryID;
2273 auto sourceID = colPart.sourceID;
2274 // enable embedding: if several colParts exist, then they are
2275 // saved as one collision
2276 if (nParts == 1 || sourceID == 0) {
2277 // FIXME:
2278 // use generators' names for generatorIDs (?)
2279 auto& header = mcReader->getMCEventHeader(sourceID, eventID);
2280 updateMCHeader(mcCollisionsCursor.cursor,
2281 hepmcXSectionsCursor.cursor,
2282 hepmcPdfInfosCursor.cursor,
2283 hepmcHeavyIonsCursor.cursor,
2284 header,
2285 iCol,
2286 bcID,
2287 time,
2288 0,
2289 sourceID);
2290 }
2291 mcColToEvSrc.emplace_back(MCColInfo{iCol, sourceID, eventID, globalBC}); // point background and injected signal events to one collision
2292 }
2293 }
2294 }
2295
2296 std::sort(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2297 [](const MCColInfo& left, const MCColInfo& right) { return (left.colIndex < right.colIndex); });
2298
2299 // vector of FDD amplitudes
2300 int16_t aFDDAmplitudesA[8] = {0u}, aFDDAmplitudesC[8] = {0u};
2301 float aFDDTimesA[8] = {0.f}, aFDDTimesC[8] = {0.f};
2302 // filling FDD table
2303 fddCursor.reserve(fddRecPoints.size());
2304 for (const auto& fddRecPoint : fddRecPoints) {
2305 for (int i = 0; i < 8; i++) {
2306 aFDDAmplitudesA[i] = 0;
2307 aFDDAmplitudesC[i] = 0;
2308 aFDDTimesA[i] = 0.f;
2309 aFDDTimesC[i] = 0.f;
2310 }
2311 uint64_t globalBC = fddRecPoint.getInteractionRecord().toLong();
2312 uint64_t bc = globalBC;
2313 auto item = bcsMap.find(bc);
2314 int bcID = -1;
2315 if (item != bcsMap.end()) {
2316 bcID = item->second;
2317 } else {
2318 LOG(fatal) << "Error: could not find a corresponding BC ID for a FDD rec. point; BC = " << bc;
2319 }
2320 const auto channelData = fddRecPoint.getBunchChannelData(fddChData);
2321 for (const auto& channel : channelData) {
2322 if (channel.mPMNumber < 8) {
2323 aFDDAmplitudesC[channel.mPMNumber] = channel.mChargeADC; // amplitude
2324 aFDDTimesC[channel.mPMNumber] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time
2325 } else {
2326 aFDDAmplitudesA[channel.mPMNumber - 8] = channel.mChargeADC; // amplitude
2327 aFDDTimesA[channel.mPMNumber - 8] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time
2328 }
2329 }
2330
2331 fddCursor(bcID,
2332 aFDDAmplitudesA,
2333 aFDDAmplitudesC,
2334 truncateFloatFraction(fddRecPoint.getCollisionTimeA() * 1E-3, mFDDTime), // ps to ns
2335 truncateFloatFraction(fddRecPoint.getCollisionTimeC() * 1E-3, mFDDTime), // ps to ns
2336 fddRecPoint.getTrigger().getTriggersignals());
2337 if (mEnableFITextra) {
2338 fddExtraCursor(bcID,
2339 aFDDTimesA,
2340 aFDDTimesC);
2341 }
2342 }
2343
2344 // filling FT0 table
2345 std::vector<float> aAmplitudesA, aAmplitudesC, aTimesA, aTimesC;
2346 std::vector<uint8_t> aChannelsA, aChannelsC;
2347 ft0Cursor.reserve(ft0RecPoints.size());
2348 for (auto& ft0RecPoint : ft0RecPoints) {
2349 aAmplitudesA.clear();
2350 aAmplitudesC.clear();
2351 aTimesA.clear();
2352 aTimesC.clear();
2353 aChannelsA.clear();
2354 aChannelsC.clear();
2355 const auto channelData = ft0RecPoint.getBunchChannelData(ft0ChData);
2356 for (auto& channel : channelData) {
2357 // TODO: switch to calibrated amplitude
2358 if (channel.QTCAmpl > 0) {
2359 constexpr int nFT0ChannelsAside = o2::ft0::Geometry::NCellsA * 4;
2360 if (channel.ChId < nFT0ChannelsAside) {
2361 aChannelsA.push_back(channel.ChId);
2362 aAmplitudesA.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude));
2363 aTimesA.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime));
2364 } else {
2365 aChannelsC.push_back(channel.ChId - nFT0ChannelsAside);
2366 aAmplitudesC.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude));
2367 aTimesC.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime));
2368 }
2369 }
2370 }
2371 uint64_t globalBC = ft0RecPoint.getInteractionRecord().toLong();
2372 uint64_t bc = globalBC;
2373 auto item = bcsMap.find(bc);
2374 int bcID = -1;
2375 if (item != bcsMap.end()) {
2376 bcID = item->second;
2377 } else {
2378 LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc;
2379 }
2380 ft0Cursor(bcID,
2381 aAmplitudesA,
2382 aChannelsA,
2383 aAmplitudesC,
2384 aChannelsC,
2385 truncateFloatFraction(ft0RecPoint.getCollisionTimeA() * 1E-3, mT0Time), // ps to ns
2386 truncateFloatFraction(ft0RecPoint.getCollisionTimeC() * 1E-3, mT0Time), // ps to ns
2387 ft0RecPoint.getTrigger().getTriggersignals());
2388 if (mEnableFITextra) {
2389 ft0ExtraCursor(bcID,
2390 aTimesA,
2391 aTimesC);
2392 }
2393 }
2394
2395 if (mUseMC) {
2396 // Fill MC collision labels using information from the primary vertexer.
2397 mcColLabelsCursor.reserve(primVerLabels.size());
2398 for (size_t ivert = 0; ivert < primVerLabels.size(); ++ivert) {
2399 const auto& label = primVerLabels[ivert];
2400
2401 // Collect all MC collision candidates matching this (sourceID, eventID) label.
2402 // In the non-embedding case there is exactly one candidate. In the embedding
2403 // case the same (sourceID, eventID) pair can appear in multiple collisions,
2404 // so we need to disambiguate.
2405 std::vector<std::pair<int32_t, int64_t>> candidates; // (colIndex, bc)
2406 for (const auto& colInfo : mcColToEvSrc) {
2407 if (colInfo.sourceID == label.getSourceID() &&
2408 colInfo.eventID == label.getEventID()) {
2409 candidates.emplace_back(colInfo.colIndex, colInfo.bc);
2410 }
2411 }
2412
2413 int32_t mcCollisionID = -1;
2414 if (candidates.size() == 1) {
2415 mcCollisionID = candidates[0].first;
2416 } else if (candidates.size() > 1) {
2417 // Disambiguate by BC: pick the MCCollision whose BC is closest
2418 // to the reconstructed collision's BC.
2419 // TODO: Consider a complementary strategy using the MC labels of tracks
2420 // associated to the primary vertex, and/or by allowing the primary
2421 // vertexer to return multiple MC collision labels per vertex.
2422 const auto& timeStamp = primVertices[ivert].getTimeStamp();
2423 const double interactionTime = timeStamp.getTimeStamp() * 1E3; // us -> ns
2424 const auto recoBC = relativeTime_to_GlobalBC(interactionTime);
2425 int64_t bestDiff = std::numeric_limits<int64_t>::max();
2426 for (const auto& [colIndex, bc] : candidates) {
2427 const auto bcDiff = std::abs(static_cast<int64_t>(bc) - static_cast<int64_t>(recoBC));
2428 if (bcDiff < bestDiff) {
2429 bestDiff = bcDiff;
2430 mcCollisionID = colIndex;
2431 }
2432 }
2433 }
2434
2435 uint16_t mcMask = 0; // TODO: set mask using normalised weights
2436 mcColLabelsCursor(mcCollisionID, mcMask);
2437 }
2438 }
2439
2440 cacheTriggers(recoData);
2441 countTPCClusters(recoData);
2442
2443 int collisionID = 0;
2444 mIndexTableMFT.resize(recoData.getMFTTracks().size());
2445 mIndexTableFwd.resize(recoData.getMCHTracks().size());
2446
2447 auto& trackReffwd = primVer2TRefs.back();
2448 fillIndexTablesPerCollision(trackReffwd, primVerGIs, recoData);
2449 collisionID = 0;
2450 for (auto& vertex : primVertices) {
2451 auto& trackReffwd = primVer2TRefs[collisionID];
2452 fillIndexTablesPerCollision(trackReffwd, primVerGIs, recoData); // this function must follow the same track order as 'fillTrackTablesPerCollision' to fill the map of track indices
2453 collisionID++;
2454 }
2455
2457 prepareStrangenessTracking(recoData);
2458
2459 mGIDToTableFwdID.clear(); // reset the tables to be used by 'fillTrackTablesPerCollision'
2460 mGIDToTableMFTID.clear();
2461
2462 if (mPropTracks || mThinTracks) {
2463 auto v0s = recoData.getV0sIdx();
2464 auto cascades = recoData.getCascadesIdx();
2465 auto decays3Body = recoData.getDecays3BodyIdx();
2466 mGIDUsedBySVtx.reserve(v0s.size() * 2 + cascades.size() + decays3Body.size() * 3);
2467 for (const auto& v0 : v0s) {
2468 mGIDUsedBySVtx.insert(v0.getProngID(0));
2469 mGIDUsedBySVtx.insert(v0.getProngID(1));
2470 }
2471 for (const auto& cascade : cascades) {
2472 mGIDUsedBySVtx.insert(cascade.getBachelorID());
2473 }
2474 for (const auto& id3Body : decays3Body) {
2475 mGIDUsedBySVtx.insert(id3Body.getProngID(0));
2476 mGIDUsedBySVtx.insert(id3Body.getProngID(1));
2477 mGIDUsedBySVtx.insert(id3Body.getProngID(2));
2478 }
2479
2480 mGIDUsedByStr.reserve(recoData.getStrangeTracks().size());
2481 for (const auto& sTrk : recoData.getStrangeTracks()) {
2482 mGIDUsedByStr.emplace(sTrk.mITSRef, GIndex::ITS);
2483 }
2484 }
2485
2486 mCurrentTRDTrigID = 0; // reinitialize index for TRD trigger record search
2487 // filling unassigned tracks first
2488 // so that all unassigned tracks are stored in the beginning of the table together
2489 auto& trackRef = primVer2TRefs.back(); // references to unassigned tracks are at the end
2490 // fixme: interaction time is undefined for unassigned tracks (?)
2491 fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, trdExtraCursor,
2492 ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor,
2493 fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap);
2494
2495 mCurrentTRDTrigID = 0; // reinitialize index for TRD trigger record search
2496 // filling collisions and tracks into tables
2497 collisionID = 0;
2498 collisionsCursor.reserve(primVertices.size());
2499 for (auto& vertex : primVertices) {
2500 auto& cov = vertex.getCov();
2501 auto& timeStamp = vertex.getTimeStamp(); // this is a relative time
2502 const double interactionTime = timeStamp.getTimeStamp() * 1E3; // mus to ns
2503 uint64_t globalBC = relativeTime_to_GlobalBC(interactionTime);
2504 uint64_t localBC = relativeTime_to_LocalBC(interactionTime);
2505 LOG(debug) << "global BC " << globalBC << " local BC " << localBC << " relative interaction time " << interactionTime;
2506 // collision timestamp in ns wrt the beginning of collision BC
2507 const float relInteractionTime = static_cast<float>(localBC * o2::constants::lhc::LHCBunchSpacingNS - interactionTime);
2508 auto item = bcsMap.find(globalBC);
2509 int bcID = -1;
2510 if (item != bcsMap.end()) {
2511 bcID = item->second;
2512 } else {
2513 LOG(fatal) << "Error: could not find a corresponding BC ID for a collision; BC = " << globalBC << ", collisionID = " << collisionID;
2514 }
2515 collisionsCursor(bcID,
2516 truncateFloatFraction(vertex.getX(), mCollisionPosition),
2517 truncateFloatFraction(vertex.getY(), mCollisionPosition),
2518 truncateFloatFraction(vertex.getZ(), mCollisionPosition),
2519 truncateFloatFraction(cov[0], mCollisionPositionCov),
2520 truncateFloatFraction(cov[1], mCollisionPositionCov),
2521 truncateFloatFraction(cov[2], mCollisionPositionCov),
2522 truncateFloatFraction(cov[3], mCollisionPositionCov),
2523 truncateFloatFraction(cov[4], mCollisionPositionCov),
2524 truncateFloatFraction(cov[5], mCollisionPositionCov),
2525 vertex.getFlags(),
2526 truncateFloatFraction(vertex.getChi2(), mCollisionPositionCov),
2527 vertex.getNContributors(),
2528 truncateFloatFraction(relInteractionTime, mCollisionPosition),
2529 truncateFloatFraction(timeStamp.getTimeStampError() * 1E3, mCollisionPositionCov));
2530 mVtxToTableCollID[collisionID] = mTableCollID++;
2531
2532 auto& trackRef = primVer2TRefs[collisionID];
2533 // passing interaction time in [ps]
2534 fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, trdExtraCursor, ambigTracksCursor,
2535 mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor,
2536 fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap);
2537 collisionID++;
2538 }
2539
2540 fillSecondaryVertices(recoData, v0sCursor, cascadesCursor, decay3BodyCursor);
2541 fillHMPID(recoData, hmpCursor);
2542 fillStrangenessTrackingTables(recoData, trackedV0Cursor, trackedCascadeCursor, tracked3BodyCurs);
2543
2544 // helper map for fast search of a corresponding class mask for a bc
2545 auto emcalIncomplete = filterEMCALIncomplete(recoData.getEMCALTriggers());
2546 std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> bcToClassMask;
2547 if (mInputSources[GID::CTP]) {
2548 LOG(debug) << "CTP input available";
2549 for (auto& ctpDigit : ctpDigits) {
2550 uint64_t bc = ctpDigit.intRecord.toLong();
2551 uint64_t classMask = ctpDigit.CTPClassMask.to_ulong();
2552 uint64_t inputMask = ctpDigit.CTPInputMask.to_ulong();
2553 if (emcalIncomplete.find(bc) != emcalIncomplete.end()) {
2554 // reject EMCAL triggers as BC was rejected as incomplete at readout level
2555 auto classMaskOrig = classMask;
2556 classMask = classMask & ~mEMCALTrgClassMask;
2557 LOG(debug) << "Found EMCAL incomplete event, mask before " << std::bitset<64>(classMaskOrig) << ", after " << std::bitset<64>(classMask);
2558 }
2559 bcToClassMask[bc] = {classMask, inputMask};
2560 // LOG(debug) << Form("classmask:0x%llx", classMask);
2561 }
2562 }
2563
2564 // filling BC table
2565 bcCursor.reserve(bcsMap.size());
2566 for (auto& item : bcsMap) {
2567 uint64_t bc = item.first;
2568 std::pair<uint64_t, uint64_t> masks{0, 0};
2569 if (mInputSources[GID::CTP]) {
2570 auto bcClassPair = bcToClassMask.find(bc);
2571 if (bcClassPair != bcToClassMask.end()) {
2572 masks = bcClassPair->second;
2573 }
2574 }
2575 bcCursor(runNumber,
2576 bc,
2577 masks.first,
2578 masks.second);
2579 }
2580
2581 bcToClassMask.clear();
2582
2583 // filling BC flags table:
2584 auto bcFlags = fillBCFlags(recoData, bcsMap);
2585 bcFlagsCursor.reserve(bcFlags.size());
2586 for (auto f : bcFlags) {
2587 bcFlagsCursor(f);
2588 }
2589
2590 // fill cpvcluster table
2591 if (mInputSources[GIndex::CPV]) {
2592 float posX, posZ;
2593 cpvClustersCursor.reserve(cpvClusters.size());
2594 for (auto& cpvEvent : cpvTrigRecs) {
2595 uint64_t bc = cpvEvent.getBCData().toLong();
2596 auto item = bcsMap.find(bc);
2597 int bcID = -1;
2598 if (item != bcsMap.end()) {
2599 bcID = item->second;
2600 } else {
2601 LOG(fatal) << "Error: could not find a corresponding BC ID for a CPV Trigger Record; BC = " << bc;
2602 }
2603 for (int iClu = cpvEvent.getFirstEntry(); iClu < cpvEvent.getFirstEntry() + cpvEvent.getNumberOfObjects(); iClu++) {
2604 auto& clu = cpvClusters[iClu];
2605 clu.getLocalPosition(posX, posZ);
2606 cpvClustersCursor(bcID,
2607 truncateFloatFraction(posX, mCPVPos),
2608 truncateFloatFraction(posZ, mCPVPos),
2609 truncateFloatFraction(clu.getEnergy(), mCPVAmpl),
2610 clu.getPackedClusterStatus());
2611 }
2612 }
2613 }
2614
2615 if (mUseMC) {
2616 TStopwatch timer;
2617 timer.Start();
2618 // filling mc particles table
2619 fillMCParticlesTable(*mcReader,
2620 mcParticlesCursor.cursor,
2621 primVer2TRefs,
2622 primVerGIs,
2623 recoData,
2624 mcColToEvSrc);
2625 timer.Stop();
2626 LOG(info) << "FILL MC took " << timer.RealTime() << " s";
2627 mcColToEvSrc.clear();
2628
2629 // ------------------------------------------------------
2630 // filling track labels
2631
2632 // need to go through labels in the same order as for tracks
2633 fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, primVer2TRefs.back(), primVerGIs, recoData);
2634 for (auto iref = 0U; iref < primVer2TRefs.size() - 1; iref++) {
2635 auto& trackRef = primVer2TRefs[iref];
2636 fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, trackRef, primVerGIs, recoData, iref);
2637 }
2638 }
2639
2640 // Fill calo tables and if MC also the MCCaloTable, therefore, has to be after fillMCParticlesTable call!
2641 if (mInputSources[GIndex::PHS] || mInputSources[GIndex::EMC]) {
2642 fillCaloTable(caloCellsCursor, caloCellsTRGTableCursor, mcCaloLabelsCursor, bcsMap, recoData);
2643 }
2644
2645 bcsMap.clear();
2646 clearMCKeepStore(mToStore);
2647 mGIDToTableID.clear();
2648 mTableTrID = 0;
2649 mGIDToTableFwdID.clear();
2650 mTableTrFwdID = 0;
2651 mGIDToTableMFTID.clear();
2652 mTableTrMFTID = 0;
2653 mVtxToTableCollID.clear();
2654 mTableCollID = 0;
2655 mV0ToTableID.clear();
2656 mTableV0ID = 0;
2657
2658 mIndexTableFwd.clear();
2659 mIndexFwdID = 0;
2660 mIndexTableMFT.clear();
2661 mIndexMFTID = 0;
2662
2663 mBCLookup.clear();
2664
2665 mGIDUsedBySVtx.clear();
2666 mGIDUsedByStr.clear();
2667
2668 originCursor(tfNumber);
2669
2670 // sending metadata to writer
2671 TString dataType = mUseMC ? "MC" : "RAW";
2672 TString O2Version = o2::fullVersion();
2673 TString ROOTVersion = ROOT_RELEASE;
2674 mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag", "CreatedBy"};
2675 mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag, mUser};
2676 add_additional_meta_info(mMetaDataKeys, mMetaDataVals);
2677
2678 if (mCollectConfigFiles) {
2679 collectConfigFiles(mMetaDataKeys, mMetaDataVals);
2680 }
2681
2682 pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, mMetaDataKeys);
2683 pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, mMetaDataVals);
2684
2685 pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber);
2686 pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, mAODParent);
2687
2688 mTimer.Stop();
2689}
2690
2691void AODProducerWorkflowDPL::cacheTriggers(const o2::globaltracking::RecoContainer& recoData)
2692{
2693 // ITS tracks->ROF
2694 {
2695 mITSROFs.clear();
2696 const auto& rofs = recoData.getITSTracksROFRecords();
2697 uint16_t count = 0;
2698 for (const auto& rof : rofs) {
2699 int first = rof.getFirstEntry(), last = first + rof.getNEntries();
2700 for (int i = first; i < last; i++) {
2701 mITSROFs.push_back(count);
2702 }
2703 count++;
2704 }
2705 }
2706 // MFT tracks->ROF
2707 {
2708 mMFTROFs.clear();
2709 const auto& rofs = recoData.getMFTTracksROFRecords();
2710 uint16_t count = 0;
2711 for (const auto& rof : rofs) {
2712 int first = rof.getFirstEntry(), last = first + rof.getNEntries();
2713 for (int i = first; i < last; i++) {
2714 mMFTROFs.push_back(count);
2715 }
2716 count++;
2717 }
2718 }
2719 // ITSTPCTRD tracks -> TRD trigger
2720 {
2721 mITSTPCTRDTriggers.clear();
2722 const auto& itstpctrigs = recoData.getITSTPCTRDTriggers();
2723 int count = 0;
2724 for (const auto& trig : itstpctrigs) {
2725 int first = trig.getFirstTrack(), last = first + trig.getNumberOfTracks();
2726 for (int i = first; i < last; i++) {
2727 mITSTPCTRDTriggers.push_back(count);
2728 }
2729 count++;
2730 }
2731 }
2732 // TPCTRD tracks -> TRD trigger
2733 {
2734 mTPCTRDTriggers.clear();
2735 const auto& tpctrigs = recoData.getTPCTRDTriggers();
2736 int count = 0;
2737 for (const auto& trig : tpctrigs) {
2738 int first = trig.getFirstTrack(), last = first + trig.getNumberOfTracks();
2739 for (int i = first; i < last; i++) {
2740 mTPCTRDTriggers.push_back(count);
2741 }
2742 count++;
2743 }
2744 }
2745 // MCH tracks->ROF
2746 {
2747 mMCHROFs.clear();
2748 const auto& rofs = recoData.getMCHTracksROFRecords();
2749 uint16_t count = 0;
2750 for (const auto& rof : rofs) {
2751 int first = rof.getFirstIdx(), last = first + rof.getNEntries();
2752 for (int i = first; i < last; i++) {
2753 mMCHROFs.push_back(count);
2754 }
2755 count++;
2756 }
2757 }
2758}
2759
2760AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrack(int collisionID, std::uint64_t collisionBC, GIndex trackIndex,
2761 const o2::globaltracking::RecoContainer& data, const std::map<uint64_t, int>& bcsMap)
2762{
2763 TrackExtraInfo extraInfoHolder;
2764 if (collisionID < 0) {
2765 extraInfoHolder.flags |= o2::aod::track::OrphanTrack;
2766 }
2767 bool needBCSlice = collisionID < 0; // track is associated to multiple vertices
2768 uint64_t bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default track time is wrt collision BC (unless no collision assigned)
2769
2770 auto setTrackTime = [&](double t, double terr, bool gaussian) {
2771 // set track time and error, for ambiguous tracks define the bcSlice as it was used in vertex-track association
2772 // 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
2773 if (!gaussian) {
2774 extraInfoHolder.flags |= o2::aod::track::TrackTimeResIsRange;
2775 }
2776 extraInfoHolder.trackTimeRes = terr;
2777 if (needBCSlice) { // need to define BC slice
2778 double error = this->mTimeMarginTrackTime + (gaussian ? extraInfoHolder.trackTimeRes * this->mNSigmaTimeTrack : extraInfoHolder.trackTimeRes);
2779 bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - error, t + error, bcsMap);
2780 }
2781 extraInfoHolder.trackTime = float(t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS);
2782 extraInfoHolder.diffBCRef = int(bcOfTimeRef);
2783 LOGP(debug, "time : {}/{} -> {}/{} -> trunc: {}/{} CollID: {} Amb: {}", t, terr, t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS, terr,
2784 truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime), truncateFloatFraction(extraInfoHolder.trackTimeRes, mTrackTimeError),
2785 collisionID, trackIndex.isAmbiguous());
2786 };
2787 auto contributorsGID = data.getSingleDetectorRefs(trackIndex);
2788 const auto& trackPar = data.getTrackParam(trackIndex);
2789 extraInfoHolder.flags |= trackPar.getPID() << 28;
2790 auto src = trackIndex.getSource();
2791 if (contributorsGID[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF
2792 const auto& tofMatch = data.getTOFMatch(trackIndex);
2793 extraInfoHolder.tofChi2 = tofMatch.getChi2();
2794 const auto& tofInt = tofMatch.getLTIntegralOut();
2795 float intLen = tofInt.getL();
2796 extraInfoHolder.length = intLen;
2797 const float mass = o2::constants::physics::MassPionCharged; // default pid = pion
2798 if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) {
2799 float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed));
2800 if (expBeta > o2::constants::math::Almost1) {
2802 }
2803 extraInfoHolder.tofExpMom = mass * expBeta / std::sqrt(1.f - expBeta * expBeta);
2804 }
2805 // correct the time of the track
2806 const double massZ = o2::track::PID::getMass2Z(trackPar.getPID());
2807 const double energy = sqrt((massZ * massZ) + (extraInfoHolder.tofExpMom * extraInfoHolder.tofExpMom));
2808 const double exp = extraInfoHolder.length * energy / (cSpeed * extraInfoHolder.tofExpMom);
2809 auto tofSignal = (tofMatch.getSignal() - exp) * 1e-3; // time in ns wrt TF start
2810 setTrackTime(tofSignal, 0.2, true); // FIXME: calculate actual resolution (if possible?)
2811 }
2812 if (contributorsGID[GIndex::Source::TRD].isIndexSet()) { // ITS-TPC-TRD-TOF, TPC-TRD-TOF, TPC-TRD, ITS-TPC-TRD
2813 const auto& trdOrig = data.getTrack<o2::trd::TrackTRD>(contributorsGID[GIndex::Source::TRD]); // refitted TRD trac
2814 extraInfoHolder.trdChi2 = trdOrig.getChi2();
2815 extraInfoHolder.trdSignal = trdOrig.getSignal();
2816 extraInfoHolder.trdPattern = getTRDPattern(trdOrig);
2817 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
2818 // TRD is triggered: time uncertainty is within a BC
2819 const auto& trdTrig = (src == GIndex::Source::TPCTRD) ? data.getTPCTRDTriggers()[mTPCTRDTriggers[trackIndex.getIndex()]] : data.getITSTPCTRDTriggers()[mITSTPCTRDTriggers[trackIndex.getIndex()]];
2820 double ttrig = trdTrig.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS; // 1st get time wrt TF start
2821 setTrackTime(ttrig, 1., true); // FIXME: calculate actual resolution (if possible?)
2822 }
2823 }
2824 if (contributorsGID[GIndex::Source::ITS].isIndexSet()) {
2825 const auto& itsTrack = data.getITSTrack(contributorsGID[GIndex::ITS]);
2826 int nClusters = itsTrack.getNClusters();
2827 float chi2 = itsTrack.getChi2();
2828 extraInfoHolder.itsChi2NCl = nClusters != 0 ? chi2 / (float)nClusters : 0;
2829 extraInfoHolder.itsClusterSizes = itsTrack.getClusterSizes();
2830 if (src == GIndex::ITS) { // standalone ITS track should set its time from the ROF
2831 const auto& rof = data.getITSTracksROFRecords()[mITSROFs[trackIndex.getIndex()]];
2832 double t = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mITSROFrameHalfLengthNS + mITSROFBiasNS;
2833 setTrackTime(t, mITSROFrameHalfLengthNS, false);
2834 }
2835 } else if (contributorsGID[GIndex::Source::ITSAB].isIndexSet()) { // this is an ITS-TPC afterburner contributor
2836 extraInfoHolder.itsClusterSizes = data.getITSABRefs()[contributorsGID[GIndex::Source::ITSAB].getIndex()].getClusterSizes();
2837 }
2838 if (contributorsGID[GIndex::Source::TPC].isIndexSet()) {
2839 const auto& tpcOrig = data.getTPCTrack(contributorsGID[GIndex::TPC]);
2840 const auto& tpcClData = mTPCCounters[contributorsGID[GIndex::TPC]];
2841 const auto& dEdx = tpcOrig.getdEdx().dEdxTotTPC > 0 ? tpcOrig.getdEdx() : tpcOrig.getdEdxAlt();
2842 if (tpcOrig.getdEdx().dEdxTotTPC == 0) {
2843 extraInfoHolder.flags |= o2::aod::track::TPCdEdxAlt;
2844 }
2845 if (tpcOrig.hasASideClusters()) {
2846 extraInfoHolder.flags |= o2::aod::track::TPCSideA;
2847 }
2848 if (tpcOrig.hasCSideClusters()) {
2849 extraInfoHolder.flags |= o2::aod::track::TPCSideC;
2850 }
2851 extraInfoHolder.tpcInnerParam = tpcOrig.getP() / tpcOrig.getAbsCharge();
2852 extraInfoHolder.tpcChi2NCl = tpcOrig.getNClusters() ? tpcOrig.getChi2() / tpcOrig.getNClusters() : 0;
2853 extraInfoHolder.tpcSignal = dEdx.dEdxTotTPC;
2854 extraInfoHolder.tpcNClsFindable = tpcOrig.getNClusters();
2855 extraInfoHolder.tpcNClsFindableMinusFound = tpcOrig.getNClusters() - tpcClData.found;
2856 extraInfoHolder.tpcNClsFindableMinusCrossedRows = tpcOrig.getNClusters() - tpcClData.crossed;
2857 extraInfoHolder.tpcNClsShared = tpcClData.shared;
2858 uint32_t clsUsedForPID = dEdx.NHitsIROC + dEdx.NHitsOROC1 + dEdx.NHitsOROC2 + dEdx.NHitsOROC3;
2859 extraInfoHolder.tpcNClsFindableMinusPID = tpcOrig.getNClusters() - clsUsedForPID;
2860 if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range
2861 if (needBCSlice) {
2862 double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value
2863 double terr = 0.5 * (tpcOrig.getDeltaTFwd() + tpcOrig.getDeltaTBwd()) * mTPCBinNS;
2864 double err = mTimeMarginTrackTime + terr;
2865 bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - err, t + err, bcsMap);
2866 }
2868 p.setDeltaTFwd(tpcOrig.getDeltaTFwd());
2869 p.setDeltaTBwd(tpcOrig.getDeltaTBwd());
2870 extraInfoHolder.trackTimeRes = p.getTimeErr();
2871 extraInfoHolder.trackTime = float(tpcOrig.getTime0() * mTPCBinNS - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS);
2872 extraInfoHolder.diffBCRef = int(bcOfTimeRef);
2873 extraInfoHolder.isTPConly = true; // no truncation
2874 extraInfoHolder.flags |= o2::aod::track::TrackTimeAsym;
2875 } else if (src == GIndex::ITSTPC) { // its-tpc matched tracks have gaussian time error and the time was not set above
2876 const auto& trITSTPC = data.getTPCITSTrack(trackIndex);
2877 auto ts = trITSTPC.getTimeMUS();
2878 setTrackTime(ts.getTimeStamp() * 1.e3, ts.getTimeStampError() * 1.e3, true);
2879 }
2880 }
2881
2882 extrapolateToCalorimeters(extraInfoHolder, data.getTrackParamOut(trackIndex));
2883 // set bit encoding for PVContributor property as part of the flag field
2884 if (trackIndex.isPVContributor()) {
2885 extraInfoHolder.flags |= o2::aod::track::PVContributor;
2886 }
2887 return extraInfoHolder;
2888}
2889
2890AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int collisionID, std::uint64_t collisionBC, GIndex trackIndex,
2891 const o2::globaltracking::RecoContainer& data, const std::map<uint64_t, int>& bcsMap)
2892{
2893 TrackQA trackQAHolder;
2894 auto contributorsGID = data.getTPCContributorGID(trackIndex);
2895 const auto& trackPar = data.getTrackParam(trackIndex);
2896 if (contributorsGID.isIndexSet()) {
2897 auto prop = o2::base::Propagator::Instance();
2898 const auto& tpcOrig = data.getTPCTrack(contributorsGID);
2901 const o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT;
2902 const o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ());
2903 std::array<float, 2> dcaInfo{-999., -999.};
2904 if (prop->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) {
2905 trackQAHolder.tpcdcaR = 100. * dcaInfo[0] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt());
2906 trackQAHolder.tpcdcaZ = 100. * dcaInfo[1] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt());
2907 }
2908 // This allows to safely clamp any float to one byte, using the
2909 // minmal/maximum values as under-/overflow borders and rounding to the nearest integer
2910 auto safeInt8Clamp = [](auto value) -> int8_t {
2911 using ValType = decltype(value);
2912 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()))));
2913 };
2914 auto safeUInt8Clamp = [](auto value) -> uint8_t {
2915 using ValType = decltype(value);
2916 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()))));
2917 };
2918
2920 uint8_t clusterCounters[8] = {0};
2921 {
2922 uint8_t sectorIndex, rowIndex;
2923 uint32_t clusterIndex;
2924 const auto& tpcClusRefs = data.getTPCTracksClusterRefs();
2925 for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) {
2926 o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, tpcOrig.getClusterRef());
2927 char indexTracklet = (rowIndex % 152) / 19;
2928 clusterCounters[indexTracklet]++;
2929 }
2930 }
2931 uint8_t byteMask = 0;
2932 for (int i = 0; i < 8; i++) {
2933 if (clusterCounters[i] > 5) {
2934 byteMask |= (1 << i);
2935 }
2936 }
2937 trackQAHolder.tpcTime0 = tpcOrig.getTime0();
2938 trackQAHolder.tpcClusterByteMask = byteMask;
2939 const auto& dEdxInfoAlt = tpcOrig.getdEdxAlt(); // tpcOrig.getdEdx()
2940 const float dEdxNorm = (dEdxInfoAlt.dEdxTotTPC > 0) ? 100. / dEdxInfoAlt.dEdxTotTPC : 0;
2941 trackQAHolder.tpcdEdxNorm = dEdxInfoAlt.dEdxTotTPC;
2942 trackQAHolder.tpcdEdxMax0R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxIROC * dEdxNorm);
2943 trackQAHolder.tpcdEdxMax1R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC1 * dEdxNorm);
2944 trackQAHolder.tpcdEdxMax2R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC2 * dEdxNorm);
2945 trackQAHolder.tpcdEdxMax3R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC3 * dEdxNorm);
2946 //
2947 trackQAHolder.tpcdEdxTot0R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotIROC * dEdxNorm);
2948 trackQAHolder.tpcdEdxTot1R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC1 * dEdxNorm);
2949 trackQAHolder.tpcdEdxTot2R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC2 * dEdxNorm);
2950 trackQAHolder.tpcdEdxTot3R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC3 * dEdxNorm);
2952 float scaleTOF{0};
2953 auto contributorsGIDA = data.getSingleDetectorRefs(trackIndex);
2954 if (contributorsGIDA[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF
2955 const auto& tofMatch = data.getTOFMatch(trackIndex);
2956 const float qpt = trackPar.getQ2Pt();
2958 trackQAHolder.dTofdX = safeInt8Clamp(tofMatch.getDXatTOF() / scaleTOF);
2959 trackQAHolder.dTofdZ = safeInt8Clamp(tofMatch.getDZatTOF() / scaleTOF);
2960 }
2961
2962 // Add matching information at a reference point (defined by
2963 // o2::aod::track::trackQARefRadius) in the same frame as the global track
2964 // without material corrections and error propagation
2965 if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) {
2966 const auto& itsOrig = data.getITSTrack(itsContGID);
2967 o2::track::TrackPar gloCopy = trackPar;
2968 o2::track::TrackPar itsCopy = itsOrig.getParamOut();
2969 o2::track::TrackPar tpcCopy = tpcOrig;
2970 if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) &&
2971 prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) &&
2972 prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) {
2973 // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track
2974 // The scale is defined by the global track scaling depending on beta0
2975 const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f));
2976 const float qpt = gloCopy.getQ2Pt();
2977 const float x = qpt / beta0;
2978 // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2)
2979 auto scaleCont = [&x](int i) -> float {
2981 };
2982 auto scaleGlo = [&x](int i) -> float {
2984 };
2985
2986 // Calculate deltas for contributors
2987 trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0));
2988 trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1));
2989 trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2));
2990 trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3));
2991 trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4));
2992 // Calculate deltas for global track against averaged contributors
2993 trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0));
2994 trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1));
2995 trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2));
2996 trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3));
2997 trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4));
2998 //
2999
3000 if (mStreamerFlags[AODProducerStreamerFlags::TrackQA]) {
3001 (*mStreamer) << "trackQA"
3002 << "trackITSOrig=" << itsOrig
3003 << "trackTPCOrig=" << tpcOrig
3004 << "trackITSTPCOrig=" << trackPar
3005 << "trackITSProp=" << itsCopy
3006 << "trackTPCProp=" << tpcCopy
3007 << "trackITSTPCProp=" << gloCopy
3008 << "refRadius=" << o2::aod::track::trackQARefRadius
3009 << "scaleBins=" << o2::aod::track::trackQAScaleBins
3010 << "scaleCont0=" << scaleCont(0)
3011 << "scaleCont1=" << scaleCont(1)
3012 << "scaleCont2=" << scaleCont(2)
3013 << "scaleCont3=" << scaleCont(3)
3014 << "scaleCont4=" << scaleCont(4)
3015 << "scaleGlo0=" << scaleGlo(0)
3016 << "scaleGlo1=" << scaleGlo(1)
3017 << "scaleGlo2=" << scaleGlo(2)
3018 << "scaleGlo3=" << scaleGlo(3)
3019 << "scaleGlo4=" << scaleGlo(4)
3020 << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0
3021 << "trackQAHolder.tpcdEdxNorm=" << trackQAHolder.tpcdEdxNorm
3022 << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR
3023 << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ
3024 << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask
3025 << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R
3026 << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R
3027 << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R
3028 << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R
3029 << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R
3030 << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R
3031 << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R
3032 << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R
3033 << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY
3034 << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ
3035 << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp
3036 << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl
3037 << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt
3038 << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY
3039 << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ
3040 << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp
3041 << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl
3042 << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt
3043 << "trackQAHolder.dTofdX=" << trackQAHolder.dTofdX
3044 << "trackQAHolder.dTofdZ=" << trackQAHolder.dTofdZ
3045 << "scaleTOF=" << scaleTOF
3046 << "\n";
3047 }
3048 }
3049 }
3050 }
3051
3052 return trackQAHolder;
3053}
3054
3055bool AODProducerWorkflowDPL::propagateTrackToPV(o2::track::TrackParametrizationWithError<float>& trackPar,
3057 int colID)
3058{
3059 o2::dataformats::DCA dcaInfo;
3060 dcaInfo.set(999.f, 999.f, 999.f, 999.f, 999.f);
3061 o2::dataformats::VertexBase v = mVtx.getMeanVertex(colID < 0 ? 0.f : data.getPrimaryVertex(colID).getZ());
3062 return o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trackPar, 2.f, mMatCorr, &dcaInfo);
3063}
3064
3065void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfoHolder, const o2::track::TrackPar& track)
3066{
3067 constexpr float XEMCAL = 440.f, XPHOS = 460.f, XEMCAL2 = XEMCAL * XEMCAL;
3068 constexpr float ETAEMCAL = 0.75; // eta of EMCAL/DCAL with margin
3069 constexpr float ZEMCALFastCheck = 460.; // Max Z (with margin to check with straightline extrapolarion)
3070 constexpr float ETADCALINNER = 0.22; // eta of the DCAL PHOS Hole (at XEMCAL)
3071 constexpr float ETAPHOS = 0.13653194; // nominal eta of the PHOS acceptance (at XPHOS): -log(tan((TMath::Pi()/2 - atan2(63, 460))/2))
3072 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
3073 constexpr float ETADCALPHOSSWITCH = (ETADCALINNER + ETAPHOS) / 2; // switch to DCAL to PHOS check if eta < this value
3074 constexpr short SNONE = 0, SEMCAL = 0x1, SPHOS = 0x2;
3075 constexpr short SECTORTYPE[18] = {
3076 SNONE, SNONE, SNONE, SNONE, // 0:3
3077 SEMCAL, SEMCAL, SEMCAL, SEMCAL, SEMCAL, SEMCAL, // 3:9 EMCAL only
3078 SNONE, SNONE, // 10:11
3079 SPHOS, // 12 PHOS only
3080 SPHOS | SEMCAL, SPHOS | SEMCAL, SPHOS | SEMCAL, // 13:15 PHOS & DCAL
3081 SEMCAL, // 16 DCAL only
3082 SNONE // 17
3083 };
3084
3086 auto prop = o2::base::Propagator::Instance();
3087 // 1st propagate to EMCAL nominal radius
3088 float xtrg = 0;
3089 // quick check with straight line propagtion
3090 if (!outTr.getXatLabR(XEMCAL, xtrg, prop->getNominalBz(), o2::track::DirType::DirOutward) ||
3091 (std::abs(outTr.getZAt(xtrg, 0)) > ZEMCALFastCheck) ||
3092 !prop->PropagateToXBxByBz(outTr, xtrg, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT)) {
3093 LOGP(debug, "preliminary step: does not reach R={} {}", XEMCAL, outTr.asString());
3094 return;
3095 }
3096 // we do not necessarilly reach wanted radius in a single propagation
3097 if ((outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY() < XEMCAL2) &&
3098 (!outTr.rotateParam(outTr.getPhi()) ||
3099 !outTr.getXatLabR(XEMCAL, xtrg, prop->getNominalBz(), o2::track::DirType::DirOutward) ||
3100 !prop->PropagateToXBxByBz(outTr, xtrg, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT))) {
3101 LOGP(debug, "does not reach R={} {}", XEMCAL, outTr.asString());
3102 return;
3103 }
3104 // rotate to proper sector
3105 int sector = o2::math_utils::angle2Sector(outTr.getPhiPos());
3106
3107 auto propExactSector = [&outTr, &sector, prop](float xprop) -> bool { // propagate exactly to xprop in the proper sector frame
3108 int ntri = 0;
3109 while (ntri < 2) {
3110 auto outTrTmp = outTr;
3111 float alpha = o2::math_utils::sector2Angle(sector);
3112 if ((std::abs(outTr.getZ()) > ZEMCALFastCheck) || !outTrTmp.rotateParam(alpha) ||
3113 !prop->PropagateToXBxByBz(outTrTmp, xprop, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT)) {
3114 LOGP(debug, "failed on rotation to {} (sector {}) or propagation to X={} {}", alpha, sector, xprop, outTrTmp.asString());
3115 return false;
3116 }
3117 // make sure we are still in the target sector
3118 int sectorTmp = o2::math_utils::angle2Sector(outTrTmp.getPhiPos());
3119 if (sectorTmp == sector) {
3120 outTr = outTrTmp;
3121 break;
3122 }
3123 sector = sectorTmp;
3124 ntri++;
3125 }
3126 if (ntri == 2) {
3127 LOGP(debug, "failed to rotate to sector, {}", outTr.asString());
3128 return false;
3129 }
3130 return true;
3131 };
3132
3133 // we are at the EMCAL X, check if we are in the good sector
3134 if (!propExactSector(XEMCAL) || SECTORTYPE[sector] == SNONE) { // propagation failed or neither EMCAL not DCAL/PHOS
3135 return;
3136 }
3137
3138 // check if we are in a good eta range
3139 float r = std::sqrt(outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY()), tg = std::atan2(r, outTr.getZ());
3140 float eta = -std::log(std::tan(0.5f * tg)), etaAbs = std::abs(eta);
3141 if (etaAbs > ETAEMCAL) {
3142 LOGP(debug, "eta = {} is off at EMCAL radius", eta, outTr.asString());
3143 return;
3144 }
3145 // are we in the PHOS hole (with margin)?
3146 if ((SECTORTYPE[sector] & SPHOS) && etaAbs < ETADCALPHOSSWITCH) { // propagate to PHOS radius
3147 if (!propExactSector(XPHOS)) {
3148 return;
3149 }
3150 r = std::sqrt(outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY());
3151 tg = std::atan2(r, outTr.getZ());
3152 eta = -std::log(std::tan(0.5f * tg));
3153 } else if (!(SECTORTYPE[sector] & SEMCAL)) { // are in the sector with PHOS only
3154 return;
3155 }
3156 extraInfoHolder.trackPhiEMCAL = outTr.getPhiPos();
3157 extraInfoHolder.trackEtaEMCAL = eta;
3158 LOGP(debug, "eta = {} phi = {} sector {} for {}", extraInfoHolder.trackEtaEMCAL, extraInfoHolder.trackPhiEMCAL, sector, outTr.asString());
3159 //
3160}
3161
3162std::set<uint64_t> AODProducerWorkflowDPL::filterEMCALIncomplete(const gsl::span<const o2::emcal::TriggerRecord> triggers)
3163{
3164 std::set<uint64_t> emcalIncompletes;
3165 for (const auto& trg : triggers) {
3166 if (trg.getTriggerBits() & o2::emcal::triggerbits::Inc) {
3167 // trigger record masked at incomplete at readout level
3168 emcalIncompletes.insert(trg.getBCData().toLong());
3169 }
3170 }
3171 return emcalIncompletes;
3172}
3173
3174void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc)
3175{
3177 static bool initOnceDone = false;
3178 if (!initOnceDone) { // this params need to be queried only once
3179 initOnceDone = true;
3180 // Note: DPLAlpideParam for ITS and MFT will be loaded by the RecoContainer
3181 mSqrtS = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS();
3182 // apply settings
3185 std::bitset<3564> bs = bcf.getBCPattern();
3186 for (auto i = 0U; i < bs.size(); i++) {
3187 if (bs.test(i)) {
3189 }
3190 }
3191
3193 mITSROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsITS.roFrameLengthTrig);
3196 mMFTROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsMFT.roFrameLengthTrig);
3198
3199 // RS FIXME: this is not yet fetched from the CCDB
3201 mTPCBinNS = elParam.ZbinWidth * 1.e3;
3202
3203 const auto& pvParams = o2::vertexing::PVertexerParams::Instance();
3204 mNSigmaTimeTrack = pvParams.nSigmaTimeTrack;
3205 mTimeMarginTrackTime = pvParams.timeMarginTrackTime * 1.e3;
3206 mFieldON = std::abs(o2::base::Propagator::Instance()->getNominalBz()) > 0.01;
3207
3208 pc.inputs().get<o2::ctp::CTPConfiguration*>("ctpconfig");
3209 if (mEnableTRDextra) {
3210 mTRDLocalGain = pc.inputs().get<o2::trd::LocalGainFactor*>("trdlocalgainfactors").get();
3211 mTRDNoiseMap = pc.inputs().get<o2::trd::NoiseStatusMCM*>("trdnoisemap").get();
3212 mTRDGainCalib = pc.inputs().get<o2::trd::CalGain*>("trdgaincalib").get(); // time dependent gain
3213 }
3214 }
3215 if (mPropTracks) {
3217 }
3218}
3219
3220//_______________________________________
3222{
3223 // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level
3224 if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) {
3225 if (matcher == ConcreteDataMatcher("GLO", "GRPMAGFIELD", 0)) {
3227 }
3228 return;
3229 }
3230 if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) {
3231 LOG(info) << "ITS Alpide param updated";
3233 par.printKeyValues();
3234 return;
3235 }
3236 if (matcher == ConcreteDataMatcher("MFT", "ALPIDEPARAM", 0)) {
3237 LOG(info) << "MFT Alpide param updated";
3239 par.printKeyValues();
3240 return;
3241 }
3242 if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) {
3243 LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString();
3244 mVtx = *(const o2::dataformats::MeanVertexObject*)obj;
3245 return;
3246 }
3247 if (matcher == ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) {
3248 // construct mask with EMCAL trigger classes for rejection of incomplete triggers
3249 auto ctpconfig = *(const o2::ctp::CTPConfiguration*)obj;
3250 mEMCALTrgClassMask = 0;
3251 for (const auto& trgclass : ctpconfig.getCTPClasses()) {
3252 if (trgclass.cluster->maskCluster[o2::detectors::DetID::EMC]) {
3253 mEMCALTrgClassMask |= trgclass.classMask;
3254 }
3255 }
3256 LOG(info) << "Loaded EMCAL trigger class mask: " << std::bitset<64>(mEMCALTrgClassMask);
3257 }
3258}
3259
3260void AODProducerWorkflowDPL::addRefGlobalBCsForTOF(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span<const GIndex>& GIndices,
3261 const o2::globaltracking::RecoContainer& data, std::map<uint64_t, int>& bcsMap)
3262{
3263 // Orphan tracks need to refer to some globalBC and for tracks with TOF this BC should be whithin an orbit
3264 // from the track abs time (to guarantee time precision). Therefore, we may need to insert some dummy globalBCs
3265 // to guarantee proper reference.
3266 // complete globalBCs by dummy entries necessary to provide BC references for TOF tracks with requested precision
3267 // to provide a reference for the time of the orphan tracks we should make sure that there are no gaps longer
3268 // than needed to store the time with sufficient precision
3269
3270 // estimate max distance in BCs between TOF time and eventual reference BC
3271 int nbitsFrac = 24 - (32 - o2::math_utils::popcount(mTrackTime)); // number of bits used to encode the fractional part of float truncated by the mask
3272 int nbitsLoss = std::max(0, int(std::log2(TOFTimePrecPS))); // allowed bit loss guaranteeing needed precision in PS
3273 assert(nbitsFrac > 1);
3274 std::uint64_t maxRangePS = std::uint64_t(0x1) << (nbitsFrac + nbitsLoss);
3275 int maxGapBC = maxRangePS / (o2::constants::lhc::LHCBunchSpacingNS * 1e3); // max gap in BCs allowing to store time with required precision
3276 LOG(info) << "Max gap of " << maxGapBC << " BCs to closest globalBC reference is needed for TOF tracks to provide precision of "
3277 << TOFTimePrecPS << " ps";
3278
3279 // check if there are tracks orphan tracks at all
3280 if (!trackRef.getEntries()) {
3281 return;
3282 }
3283 // the bscMap has at least TF start BC
3284 std::uint64_t maxBC = mStartIR.toLong();
3285 const auto& tofClus = data.getTOFClusters();
3286 for (int src = GIndex::NSources; src--;) {
3287 if (!GIndex::getSourceDetectorsMask(src)[o2::detectors::DetID::TOF]) { // check only tracks with TOF contribution
3288 continue;
3289 }
3290 int start = trackRef.getFirstEntryOfSource(src);
3291 int end = start + trackRef.getEntriesOfSource(src);
3292 for (int ti = start; ti < end; ti++) {
3293 auto& trackIndex = GIndices[ti];
3294 const auto& tofMatch = data.getTOFMatch(trackIndex);
3295 const auto& tofInt = tofMatch.getLTIntegralOut();
3296 float intLen = tofInt.getL();
3297 float tofExpMom = 0.;
3298 if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) {
3299 float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed));
3300 if (expBeta > o2::constants::math::Almost1) {
3302 }
3303 tofExpMom = o2::constants::physics::MassPionCharged * expBeta / std::sqrt(1.f - expBeta * expBeta);
3304 } else {
3305 continue;
3306 }
3307 double massZ = o2::track::PID::getMass2Z(data.getTrackParam(trackIndex).getPID());
3308 double energy = sqrt((massZ * massZ) + (tofExpMom * tofExpMom));
3309 double exp = intLen * energy / (cSpeed * tofExpMom);
3310 auto tofSignal = (tofMatch.getSignal() - exp) * 1e-3; // time in ns wrt TF start
3311 auto bc = relativeTime_to_GlobalBC(tofSignal);
3312
3313 auto it = bcsMap.lower_bound(bc);
3314 if (it == bcsMap.end() || it->first > bc + maxGapBC) {
3315 bcsMap.emplace_hint(it, bc, 1);
3316 LOG(debug) << "adding dummy BC " << bc;
3317 }
3318 if (bc > maxBC) {
3319 maxBC = bc;
3320 }
3321 }
3322 }
3323 // make sure there is a globalBC exceeding the max encountered bc
3324 if ((--bcsMap.end())->first <= maxBC) {
3325 bcsMap.emplace_hint(bcsMap.end(), maxBC + 1, 1);
3326 }
3327 // renumber BCs
3328 int bcID = 0;
3329 for (auto& item : bcsMap) {
3330 item.second = bcID;
3331 bcID++;
3332 }
3333}
3334
3335std::uint64_t AODProducerWorkflowDPL::fillBCSlice(int (&slice)[2], double tmin, double tmax, const std::map<uint64_t, int>& bcsMap) const
3336{
3337 // for ambiguous tracks (no or multiple vertices) we store the BC slice corresponding to track time window used for track-vertex matching,
3338 // see VertexTrackMatcher::extractTracks creator method, i.e. central time estimated +- uncertainty defined as:
3339 // 1) for tracks having a gaussian time error: PVertexerParams.nSigmaTimeTrack * trackSigma + PVertexerParams.timeMarginTrackTime
3340 // 2) for tracks having time uncertainty in a fixed time interval (TPC,ITS,MFT..): half of the interval + PVertexerParams.timeMarginTrackTime
3341 // The track time in the TrackExtraInfo is stored in ns wrt the collision BC for unambigous tracks and wrt bcSlice[0] for ambiguous ones,
3342 // with convention for errors: trackSigma in case (1) and half of the time interval for case (2) above.
3343
3344 // find indices of widest slice of global BCs in the map compatible with provided BC range. bcsMap is guaranteed to be non-empty.
3345 // We also assume that tmax >= tmin.
3346
3347 uint64_t bcMin = relativeTime_to_GlobalBC(tmin), bcMax = relativeTime_to_GlobalBC(tmax);
3348
3349 /*
3350 // brute force way of searching bcs via direct binary search in the map
3351 auto lower = bcsMap.lower_bound(bcMin), upper = bcsMap.upper_bound(bcMax);
3352
3353 if (lower == bcsMap.end()) {
3354 --lower;
3355 }
3356 if (upper != lower) {
3357 --upper;
3358 }
3359 slice[0] = std::distance(bcsMap.begin(), lower);
3360 slice[1] = std::distance(bcsMap.begin(), upper);
3361 */
3362
3363 // faster way to search in bunch crossing via the accelerated bunch crossing lookup structure
3364 auto p = mBCLookup.lower_bound(bcMin);
3365 // assuming that bcMax will be >= bcMin and close to bcMin; we can find
3366 // the upper bound quickly by lineary iterating from p.first to the point where
3367 // the time becomes larger than bcMax.
3368 // (if this is not the case we could determine it with a similar call to mBCLookup)
3369 auto& bcvector = mBCLookup.getBCTimeVector();
3370 auto upperindex = p.first;
3371 while (upperindex < bcvector.size() && bcvector[upperindex] <= bcMax) {
3372 upperindex++;
3373 }
3374 if (upperindex != p.first) {
3375 upperindex--;
3376 }
3377 slice[0] = p.first;
3378 slice[1] = upperindex;
3379
3380 auto bcOfTimeRef = p.second - this->mStartIR.toLong();
3381 LOG(debug) << "BC slice t:" << tmin << " " << slice[0]
3382 << " t: " << tmax << " " << slice[1]
3383 << " bcref: " << bcOfTimeRef;
3384 return bcOfTimeRef;
3385}
3386
3387std::vector<uint8_t> AODProducerWorkflowDPL::fillBCFlags(const o2::globaltracking::RecoContainer& data, std::map<uint64_t, int>& bcsMap) const
3388{
3389 std::vector<uint8_t> flags(bcsMap.size());
3390
3391 // flag BCs belonging to UPC mode ITS ROFs
3392 auto bcIt = bcsMap.begin();
3393 auto itsrofs = data.getITSTracksROFRecords();
3396 for (auto& rof : itsrofs) {
3397 if (!rof.getFlag(o2::itsmft::ROFRecord::VtxUPCMode)) {
3398 continue;
3399 }
3400 uint64_t globalBC0 = rof.getBCData().toLong() + bROF, globalBC1 = globalBC0 + lROF - 1;
3401 // BCs are sorted, iterate until the start of ROF
3402 while (bcIt != bcsMap.end()) {
3403 if (bcIt->first < globalBC0) {
3404 ++bcIt;
3405 continue;
3406 }
3407 if (bcIt->first > globalBC1) {
3408 break;
3409 }
3410 flags[bcIt->second] |= o2::aod::bc::ITSUPCMode;
3411 ++bcIt;
3412 }
3413 }
3414 return flags;
3415}
3416
3417bool AODProducerWorkflowDPL::collectConfigFiles(std::vector<TString>& keys, std::vector<TString>& values, int indent)
3418{
3419 // collect JSON-files of ConfigParams dumped by different upstream processors and add to medata
3420 static std::string pattern, directory;
3421 static size_t cachedNumberOfFiles = 0, cachedTotalFileSize = 0;
3422 static bool first = true, discard = false;
3423 if (discard) {
3424 return false;
3425 }
3426 std::error_code ec;
3427 if (first) {
3428 first = false;
3429 pattern = o2::base::NameConf::Instance().getConfigOutputFileName("*");
3431 if (dir == "/dev/null") {
3432 LOGP(warn, "ConfigParams output is disabled, abandoning {} files collection for metadata", pattern);
3433 discard = true;
3434 return false;
3435 }
3436 directory = (dir.empty() || dir == "none") ? "." : dir;
3437 if (!std::filesystem::is_directory(directory, ec)) {
3438 LOGP(error, R"(No directory "{}" is found to look for {} configuration files)", directory, pattern);
3439 discard = true;
3440 return false;
3441 }
3442 }
3443 static std::unordered_map<std::string, std::string> cachedMap;
3444 std::vector<std::filesystem::path> files;
3445 size_t currentTotalFileSize = 0;
3446
3447 for (const auto& entry : std::filesystem::directory_iterator(directory)) {
3448 if (!entry.is_regular_file()) {
3449 continue;
3450 }
3451 const std::string fileName = entry.path().filename().string();
3452 if (fnmatch(pattern.c_str(), fileName.c_str(), 0) != 0) {
3453 continue;
3454 }
3455 const auto fileSize = entry.file_size(ec);
3456 if (ec) {
3457 LOGP(error, "Cannot determine size of file {}, reason: {}", entry.path().string(), ec.message());
3458 }
3459 files.push_back(entry.path());
3460 currentTotalFileSize += static_cast<size_t>(fileSize);
3461 }
3462
3463 if (files.size() != cachedNumberOfFiles || currentTotalFileSize != cachedTotalFileSize) { // need to create a new map
3464 cachedNumberOfFiles = files.size();
3465 cachedTotalFileSize = currentTotalFileSize;
3466 cachedMap.clear();
3467 }
3468
3469 if (!files.empty() && cachedMap.empty()) {
3470 for (const auto& fname : files) {
3471 std::ifstream input(fname);
3472 if (!input) {
3473 LOGP(error, "Cannot open JSON file {}", fname.string());
3474 cachedTotalFileSize = 0; // will trigger a new trial next time
3475 continue;
3476 }
3477 nlohmann::json document;
3478 try {
3479 input >> document;
3480 } catch (const nlohmann::json::parse_error& e) {
3481 LOGP(error, "Cannot parse JSON file {}, reason: {}", fname.string(), e.what());
3482 cachedTotalFileSize = 0; // will trigger a new trial next time
3483 continue;
3484 }
3485
3486 if (!document.is_object()) {
3487 LOGP(error, "Top-level JSON value is not an object in file: {}", fname.string());
3488 cachedTotalFileSize = 0; // will trigger a new trial next time
3489 continue;
3490 }
3491
3492 for (auto it = document.begin(); it != document.end(); ++it) {
3493 const std::string& key = it.key();
3494 if (cachedMap.find(key) != cachedMap.end()) {
3495 LOGP(error, "Duplicate top-level key {} in file {}", key, fname.string());
3496 continue;
3497 }
3498 LOGP(info, "Adding json config {} from file {} to AOD metadata", key, fname.string());
3499 nlohmann::json valueDocument = nlohmann::json::object();
3500 valueDocument[key] = it.value();
3501 cachedMap[key] = valueDocument.dump(indent);
3502 }
3503 }
3504 }
3505
3506 for (const auto& kv : cachedMap) {
3507 keys.push_back(kv.first.c_str());
3508 values.push_back(kv.second.c_str());
3509 }
3510 return true;
3511}
3512
3514{
3515 LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots",
3516 mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1);
3517
3518 mStreamer.reset();
3519}
3520
3521DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun, bool enableFITextra, bool enableTRDextra)
3522{
3523 auto dataRequest = std::make_shared<DataRequest>();
3524 dataRequest->inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", CTPConfigPerRun));
3525
3526 dataRequest->requestTracks(src, useMC);
3527 dataRequest->requestPrimaryVertices(useMC);
3528 if (src[GID::CTP]) {
3529 dataRequest->requestCTPDigits(useMC);
3530 }
3531 if (enableSV) {
3532 dataRequest->requestSecondaryVertices(useMC);
3533 }
3534 if (enableStrangenessTracking) {
3535 dataRequest->requestStrangeTracks(useMC);
3536 LOGF(info, "requestStrangeTracks Finish");
3537 }
3538 if (src[GID::ITS]) {
3539 dataRequest->requestClusters(GIndex::getSourcesMask("ITS"), false);
3540 }
3541 if (src[GID::TPC]) {
3542 dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks
3543 }
3544 if (src[GID::TOF]) {
3545 dataRequest->requestTOFClusters(useMC);
3546 }
3547 if (src[GID::PHS]) {
3548 dataRequest->requestPHOSCells(useMC);
3549 }
3550 if (src[GID::TRD]) {
3551 dataRequest->requestTRDTracklets(false);
3552 }
3553 if (src[GID::EMC]) {
3554 dataRequest->requestEMCALCells(useMC);
3555 }
3556 if (src[GID::CPV]) {
3557 dataRequest->requestCPVClusters(useMC);
3558 }
3559
3560 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(true, // orbitResetTime
3561 true, // GRPECS=true
3562 true, // GRPLHCIF
3563 true, // GRPMagField
3564 true, // askMatLUT
3566 dataRequest->inputs,
3567 true); // query only once all objects except mag.field
3568
3569 dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1));
3570
3571 using namespace o2::aod;
3572 using namespace o2::aodproducer;
3573
3574 std::vector<OutputSpec> outputs{
3608 OutputSpec{"TFN", "TFNumber"},
3609 OutputSpec{"TFF", "TFFilename"},
3610 OutputSpec{"AMD", "AODMetadataKeys"},
3611 OutputSpec{"AMD", "AODMetadataVals"}};
3613 if (enableTRDextra) {
3614 outputs.push_back(OutputForTable<TRDsExtra>::spec());
3615 dataRequest->inputs.emplace_back("trdlocalgainfactors", "TRD", "LOCALGAINFACTORS", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/LocalGainFactor"));
3616 dataRequest->inputs.emplace_back("trdnoisemap", "TRD", "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/NoiseMapMCM"));
3617 dataRequest->inputs.emplace_back("trdgaincalib", "TRD", "CALGAIN", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CalGain"));
3618 }
3619
3620 if (useMC) {
3621 outputs.insert(outputs.end(),
3622 {OutputForTable<McCollisions>::spec(),
3623 OutputForTable<HepMCXSections>::spec(),
3624 OutputForTable<HepMCPdfInfos>::spec(),
3625 OutputForTable<HepMCHeavyIons>::spec(),
3626 OutputForTable<McMFTTrackLabels>::spec(),
3627 OutputForTable<McFwdTrackLabels>::spec(),
3628 OutputForTable<StoredMcParticles_001>::spec(),
3629 OutputForTable<McTrackLabels>::spec(),
3630 OutputForTable<McCaloLabels_001>::spec(),
3631 // todo: use addTableToOuput helper?
3632 // currently the description is MCCOLLISLABEL, so
3633 // the name in AO2D would be O2mccollislabel
3634 // addTableToOutput<McCollisionLabels>(outputs);
3635 {OutputLabel{"McCollisionLabels"}, "AOD", "MCCOLLISIONLABEL", 0, Lifetime::Timeframe}});
3636 }
3637
3638 return DataProcessorSpec{
3639 "aod-producer-workflow",
3640 dataRequest->inputs,
3641 outputs,
3642 AlgorithmSpec{adaptFromTask<AODProducerWorkflowDPL>(src, dataRequest, ggRequest, enableSV, useMC, enableFITextra, enableTRDextra)},
3643 Options{
3644 ConfigParamSpec{"run-number", VariantType::Int64, -1L, {"The run-number. If left default we try to get it from DPL header."}},
3645 ConfigParamSpec{"aod-timeframe-id", VariantType::Int64, -1L, {"Set timeframe number"}},
3646 ConfigParamSpec{"fill-calo-cells", VariantType::Int, 1, {"Fill calo cells into cell table"}},
3647 ConfigParamSpec{"enable-truncation", VariantType::Int, 1, {"Truncation parameter: 1 -- on, != 1 -- off"}},
3648 ConfigParamSpec{"lpmp-prod-tag", VariantType::String, "", {"LPMProductionTag"}},
3649 ConfigParamSpec{"anchor-pass", VariantType::String, "", {"AnchorPassName"}},
3650 ConfigParamSpec{"anchor-prod", VariantType::String, "", {"AnchorProduction"}},
3651 ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}},
3652 ConfigParamSpec{"aod-parent", VariantType::String, "", {"Parent AOD file name (if any)"}},
3653 ConfigParamSpec{"created-by", VariantType::String, "", {"Who created this AO2D"}},
3654 ConfigParamSpec{"nthreads", VariantType::Int, std::max(1, int(std::thread::hardware_concurrency() / 2)), {"Number of threads"}},
3655 ConfigParamSpec{"reco-mctracks-only", VariantType::Int, 0, {"Store only reconstructed MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}},
3656 ConfigParamSpec{"ctpreadout-create", VariantType::Int, 0, {"Create CTP digits from detector readout and CTP inputs. !=1 -- off, 1 -- on"}},
3657 ConfigParamSpec{"emc-select-leading", VariantType::Bool, false, {"Flag to select if only the leading contributing particle for an EMCal cell should be stored"}},
3658 ConfigParamSpec{"propagate-tracks", VariantType::Bool, false, {"Propagate tracks (not used for secondary vertices) to IP"}},
3659 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)"}},
3660 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)"}},
3661 ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}},
3662 ConfigParamSpec{"store-all-mft-cov", VariantType::Bool, false, {"Store covariance matrices for all MFT tracks"}},
3663 ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}},
3664 ConfigParamSpec{"trackqc-keepglobaltracks", VariantType::Bool, false, {"Always keep TrackQA for global tracks"}},
3665 ConfigParamSpec{"trackqc-retainonlydedx", VariantType::Bool, false, {"Keep only dEdx information, zero out everything else"}},
3666 ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}},
3667 ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}},
3668 ConfigParamSpec{"trackqc-tpc-dca", VariantType::Float, 3.f, {"Keep TPC standalone track with this DCAxy to the PV"}},
3669 ConfigParamSpec{"trackqc-tpc-cls", VariantType::Int, 80, {"Keep TPC standalone track with this #clusters"}},
3670 ConfigParamSpec{"trackqc-tpc-pt", VariantType::Float, 0.2f, {"Keep TPC standalone track with this pt"}},
3671 ConfigParamSpec{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}},
3672 ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}},
3673 ConfigParamSpec{"mc-signal-filt", VariantType::Bool, false, {"Enable usage of signal filtering (only for MC with embedding)"}},
3674 ConfigParamSpec{"collect-config-files", VariantType::Bool, false, {"Collect ConfigParams json files written by upsteam processors"}},
3675 }};
3676}
3677
3678} // namespace o2::aodproducer
Class to refer to the reconstructed information.
Definition of the 32 Central Trigger System (CTS) Trigger Types defined in https://twiki....
General auxilliary methods.
definition of CTPConfiguration and related CTP structures
Wrapper container for different reconstructed object types.
Definition of the MCH cluster minimal structure.
Reconstructed MID track.
Base track model for the Barrel, params only, w/o covariance.
uint64_t exp(uint64_t base, uint8_t exp) noexcept
definition of CTPDigit, CTPInputDigit
std::ostringstream debug
int16_t charge
Definition RawEventData.h:5
uint64_t vertex
Definition RawEventData.h:9
uint64_t bc
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
Definition of the FIT RecPoints class.
Definition of the FV0 RecPoints class.
int32_t i
std::string getType()
Definition Utils.h:138
Header of the AggregatedRunInfo struct.
Global Forward Muon tracks.
Global index for barrel track: provides provenance (detectors combination), index in respective array...
Definition of the MCTrack class.
Definition of a container to keep Monte Carlo truth external to simulation objects.
Utility functions for MC particles.
Definition of the MCH ROFrame record.
Class to perform MFT MCH (and MID) matching.
Class to store the output of the matching to HMPID.
Class to perform TOF matching to global tracks.
Definition of the 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)
static constexpr float MAX_SIN_PHI
Definition Propagator.h:72
static constexpr float MAX_STEP
Definition Propagator.h:73
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:178
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:58
static constexpr ID ITS
Definition DetID.h:63
static constexpr ID MFT
Definition DetID.h:71
static constexpr ID TPC
Definition DetID.h:64
static constexpr ID EMC
Definition DetID.h:69
static constexpr ID TOF
Definition DetID.h:66
Handler for EMCAL event data.
void reset()
Reset containers with empty ranges.
void setCellData(CellRange cells, TriggerRange triggers)
Setting the data at cell level.
void setCellMCTruthContainer(const o2::dataformats::MCTruthContainer< o2::emcal::MCLabel > *mclabels)
Setting the pointer for the MCTruthContainer for cells.
InteractionRecord getInteractionRecordForEvent(int eventID) const
Get the interaction record for the given event.
int getNumberOfEvents() const
Get the number of events handled by the event handler.
void snapshot(const Output &spec, T const &object)
ConfigParamRegistry const & options()
Definition InitContext.h:33
decltype(auto) get(R binding, int part=0) const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
ServiceRegistryRef services()
The services registry associated with this processing context.
static constexpr int NCellsA
Definition Geometry.h:52
o2::dataformats::GlobalFwdTrack MCHtoFwd(const o2::mch::TrackParam &mchTrack)
Converts mchTrack parameters to Forward coordinate system.
static bool extrapToZ(TrackParam &trackParam, double zEnd)
static void setField()
static bool extrapToVertex(TrackParam &trackParam, double xVtx, double yVtx, double zVtx, double errXVtx, double errYVtx)
Definition TrackExtrap.h:62
static bool extrapToVertexWithoutBranson(TrackParam &trackParam, double zVtx, double xUpstream=0., double yUpstream=0., std::optional< double > zUpstream=std::nullopt)
Definition TrackExtrap.h:74
track parameters for internal use
Definition TrackParam.h:34
Double_t getNonBendingCoor() const
return non bending coordinate (cm)
Definition TrackParam.h:51
Double_t getBendingCoor() const
return bending coordinate (cm)
Definition TrackParam.h:59
void setCellMCTruthContainer(const o2::dataformats::MCTruthContainer< o2::phos::MCLabel > *mclabels)
Setting the pointer for the MCTruthContainer for cells.
void setCellData(CellRange cells, TriggerRange triggers)
Setting the data at cell level.
void reset()
Reset containers with empty ranges.
InteractionRecord getInteractionRecordForEvent(int eventID) const
int getNumberOfEvents() const
MCTrack const * getTrack(o2::MCCompLabel const &) const
void releaseTracksForSourceAndEvent(int source, int event)
API to ask releasing tracks (freeing memory) for source + event.
std::vector< MCTrack > const & getTracks(int source, int event) const
variant returning all tracks for source and event at once
static void addInteractionBC(int bc, bool fromCollisonCotext=false)
Definition Utils.cxx:52
static constexpr ID Pion
Definition PID.h:96
Double_t getX() const
Definition TrackFwd.h:58
float getMPVdEdx(int iDet, bool defaultAvg=true) const
Definition CalGain.h:36
Simple noise status bit for each MCM of the TRD.
bool isTrackletFromNoisyMCM(const Tracklet64 &trklt) const
T getValue(int roc, int col, int row) const
void set(const std::string &s, int base=DefaultBase)
Definition EnumFlags.h: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
GLdouble f
Definition glcorearb.h:310
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
GLboolean * data
Definition glcorearb.h:298
GLintptr offset
Definition glcorearb.h:660
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLfloat v0
Definition glcorearb.h:811
GLbitfield flags
Definition glcorearb.h:1570
GLboolean r
Definition glcorearb.h:1233
GLuint start
Definition glcorearb.h:469
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
constexpr std::array< float, 2 > trackQAScaledTOF
Definition DataTypes.h: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)
constexpr int LHCMaxBunches
constexpr double LHCBunchSpacingNS
constexpr float Almost1
constexpr double MassPionCharged
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
auto get(const std::byte *buffer, size_t=0)
Definition DataHeader.h:454
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 float MPVDEDXDEFAULT
default Most Probable Value of TRD dEdx
Definition Constants.h:84
constexpr uint32_t Cal
Definition Triggers.h:32
const int TDCSignal[NTDCChannels]
Definition Constants.h:181
constexpr int NTDCChannels
Definition Constants.h:90
constexpr int NChannels
Definition Constants.h:65
std::string fullVersion()
get full version information (official O2 release and git commit)
GPUReconstruction * rec
helper struct to keep mapping of colIndex to MC labels and bunch crossing
static constexpr auto spec()
decltype(FFL(std::declval< cursor_t >())) cursor
const U & getTrack(int src, int id) const
GlobalIDSet getSingleDetectorRefs(GTrackID gidx) const
const o2::tpc::TrackTPC & getTPCTrack(GTrackID id) const
gsl::span< const o2::trd::CalibratedTracklet > getTRDCalibratedTracklets() const
gsl::span< const o2::trd::TriggerRecord > getTRDTriggerRecords() const
const o2::dataformats::TrackTPCITS & getTPCITSTrack(GTrackID gid) const
void collectData(o2::framework::ProcessingContext &pc, const DataRequest &request)
gsl::span< const o2::trd::Tracklet64 > getTRDTracklets() const
static bool downsampleTsallisCharged(float pt, float factorPt, float sqrts, float &weight, float rnd, float mass=0.13957)
Definition Tsallis.cxx:31
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
Cluster clu
std::uniform_int_distribution< unsigned long long > distr
std::random_device rd
std::array< uint16_t, 5 > pattern