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