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
1069 }
1070}
1071
1072template <typename MCTrackLabelCursorType, typename MCMFTTrackLabelCursorType, typename MCFwdTrackLabelCursorType>
1073void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor,
1074 MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor,
1075 MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor,
1076 const o2::dataformats::VtxTrackRef& trackRef,
1077 const gsl::span<const GIndex>& primVerGIs,
1079 int vertexId)
1080{
1081 // labelMask (temporary) usage:
1082 // bit 13 -- ITS/TPC with ITS label (track of AB tracklet) different from TPC
1083 // bit 14 -- isNoise() == true
1084 // 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)
1085 // labelID = -1 -- label is not set
1086
1087 for (int src = GIndex::NSources; src--;) {
1088 int start = trackRef.getFirstEntryOfSource(src);
1089 int end = start + trackRef.getEntriesOfSource(src);
1090 mcMFTTrackLabelCursor.reserve(end - start + mcMFTTrackLabelCursor.lastIndex());
1091 mcFwdTrackLabelCursor.reserve(end - start + mcFwdTrackLabelCursor.lastIndex());
1092 mcTrackLabelCursor.reserve(end - start + mcTrackLabelCursor.lastIndex());
1093 for (int ti = start; ti < end; ti++) {
1094 const auto trackIndex = primVerGIs[ti];
1095
1096 // check if the label was already stored (or the track was rejected for some reason in the fillTrackTablesPerCollision)
1097 auto needToStore = [trackIndex](std::unordered_map<GIndex, int>& mp) {
1098 auto entry = mp.find(trackIndex);
1099 if (entry == mp.end() || entry->second == -1) {
1100 return false;
1101 }
1102 entry->second = -1;
1103 return true;
1104 };
1105
1106 if (GIndex::includesSource(src, mInputSources)) {
1107 auto mcTruth = data.getTrackMCLabel(trackIndex);
1108 MCLabels labelHolder{};
1109 if ((src == GIndex::Source::MFT) || (src == GIndex::Source::MFTMCH) || (src == GIndex::Source::MCH) || (src == GIndex::Source::MCHMID)) { // treating mft and fwd labels separately
1110 if (!needToStore(src == GIndex::Source::MFT ? mGIDToTableMFTID : mGIDToTableFwdID)) {
1111 continue;
1112 }
1113 if (mcTruth.isValid()) { // if not set, -1 will be stored
1114 labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()];
1115 }
1116 if (mcTruth.isFake()) {
1117 labelHolder.fwdLabelMask |= (0x1 << 7);
1118 }
1119 if (mcTruth.isNoise()) {
1120 labelHolder.fwdLabelMask |= (0x1 << 6);
1121 }
1122 if (src == GIndex::Source::MFT) {
1123 mcMFTTrackLabelCursor(labelHolder.labelID,
1124 labelHolder.fwdLabelMask);
1125 } else {
1126 mcFwdTrackLabelCursor(labelHolder.labelID,
1127 labelHolder.fwdLabelMask);
1128 }
1129 } else {
1130 if (!needToStore(mGIDToTableID)) {
1131 continue;
1132 }
1133 if (mcTruth.isValid()) { // if not set, -1 will be stored
1134 labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; // defined by TPC if it contributes, otherwise: by ITS
1135 if (mcTruth.isFake()) {
1136 labelHolder.labelMask |= (0x1 << 15);
1137 }
1138 if (trackIndex.includesDet(DetID::TPC) && trackIndex.getSource() != GIndex::Source::TPC) { // this is global track
1139 auto contributorsGID = data.getSingleDetectorRefs(trackIndex);
1140 if (contributorsGID[GIndex::Source::ITSTPC].isIndexSet()) { // there is a match to ITS tracks or ITSAB tracklet!
1141 if (data.getTrackMCLabel(contributorsGID[GIndex::Source::ITSTPC]).isFake()) {
1142 labelHolder.labelMask |= (0x1 << 13);
1143 }
1144 }
1145 }
1146 if (trackIndex.includesDet(DetID::ITS)) {
1147 auto itsGID = data.getITSContributorGID(trackIndex);
1148 auto itsSource = itsGID.getSource();
1149 if (itsSource == GIndex::ITS) {
1150 auto& itsTrack = data.getITSTrack(itsGID);
1151 for (unsigned int iL = 0; iL < 7; ++iL) {
1152 if (itsTrack.isFakeOnLayer(iL)) {
1153 labelHolder.labelMask |= (0x1 << iL);
1154 }
1155 }
1156 } else if (itsSource == GIndex::ITSAB) {
1157 labelHolder.labelMask |= (data.getTrackMCLabel(itsGID).isFake() << 12);
1158 }
1159 }
1160
1161 } else if (mcTruth.isNoise()) {
1162 labelHolder.labelMask |= (0x1 << 14);
1163 }
1164 mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask);
1165 }
1166 }
1167 }
1168 }
1169
1170 // filling the tables with the strangeness tracking labels
1171 auto sTrackLabels = data.getStrangeTracksMCLabels();
1172 // check if vertexId and vertexId + 1 maps into mVertexStrLUT
1173 if (!(vertexId < 0 || vertexId >= mVertexStrLUT.size() - 1)) {
1174 mcTrackLabelCursor.reserve(mVertexStrLUT[vertexId + 1] + mcTrackLabelCursor.lastIndex());
1175 for (int iS{mVertexStrLUT[vertexId]}; iS < mVertexStrLUT[vertexId + 1]; ++iS) {
1176 auto& collStrTrk = mCollisionStrTrk[iS];
1177 auto& label = sTrackLabels[collStrTrk.second];
1178 MCLabels labelHolder;
1179 labelHolder.labelID = label.isValid() ? (mToStore[label.getSourceID()][label.getEventID()])[label.getTrackID()] : -1;
1180 labelHolder.labelMask = (label.isFake() << 15) | (label.isNoise() << 14);
1181 mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask);
1182 }
1183 }
1184}
1185
1186template <typename V0CursorType, typename CascadeCursorType, typename Decay3BodyCursorType>
1187void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::RecoContainer& recoData, V0CursorType& v0Cursor, CascadeCursorType& cascadeCursor, Decay3BodyCursorType& decay3BodyCursor)
1188{
1189
1190 auto v0s = recoData.getV0sIdx();
1191 auto cascades = recoData.getCascadesIdx();
1192 auto decays3Body = recoData.getDecays3BodyIdx();
1193
1194 v0Cursor.reserve(v0s.size());
1195 // filling v0s table
1196 for (size_t iv0 = 0; iv0 < v0s.size(); iv0++) {
1197 const auto& v0 = v0s[iv0];
1198 auto trPosID = v0.getProngID(0);
1199 auto trNegID = v0.getProngID(1);
1200 uint8_t v0flags = v0.getBits();
1201 int posTableIdx = -1, negTableIdx = -1, collID = -1;
1202 auto item = mGIDToTableID.find(trPosID);
1203 if (item != mGIDToTableID.end()) {
1204 posTableIdx = item->second;
1205 } else {
1206 LOG(warn) << "Could not find a positive track index for prong ID " << trPosID;
1207 }
1208 item = mGIDToTableID.find(trNegID);
1209 if (item != mGIDToTableID.end()) {
1210 negTableIdx = item->second;
1211 } else {
1212 LOG(warn) << "Could not find a negative track index for prong ID " << trNegID;
1213 }
1214 auto itemV = mVtxToTableCollID.find(v0.getVertexID());
1215 if (itemV == mVtxToTableCollID.end()) {
1216 LOG(warn) << "Could not find V0 collisionID for the vertex ID " << v0.getVertexID();
1217 } else {
1218 collID = itemV->second;
1219 }
1220 if (posTableIdx != -1 and negTableIdx != -1 and collID != -1) {
1221 v0Cursor(collID, posTableIdx, negTableIdx, v0flags);
1222 mV0ToTableID[int(iv0)] = mTableV0ID++;
1223 }
1224 }
1225
1226 // filling cascades table
1227 cascadeCursor.reserve(cascades.size());
1228 for (auto& cascade : cascades) {
1229 auto itemV0 = mV0ToTableID.find(cascade.getV0ID());
1230 if (itemV0 == mV0ToTableID.end()) {
1231 continue;
1232 }
1233 int v0tableID = itemV0->second, bachTableIdx = -1, collID = -1;
1234 auto bachelorID = cascade.getBachelorID();
1235 auto item = mGIDToTableID.find(bachelorID);
1236 if (item != mGIDToTableID.end()) {
1237 bachTableIdx = item->second;
1238 } else {
1239 LOG(warn) << "Could not find a bachelor track index";
1240 continue;
1241 }
1242 auto itemV = mVtxToTableCollID.find(cascade.getVertexID());
1243 if (itemV != mVtxToTableCollID.end()) {
1244 collID = itemV->second;
1245 } else {
1246 LOG(warn) << "Could not find cascade collisionID for the vertex ID " << cascade.getVertexID();
1247 continue;
1248 }
1249 cascadeCursor(collID, v0tableID, bachTableIdx);
1250 }
1251
1252 // filling 3 body decays table
1253 decay3BodyCursor.reserve(decays3Body.size());
1254 for (size_t i3Body = 0; i3Body < decays3Body.size(); i3Body++) {
1255 const auto& decay3Body = decays3Body[i3Body];
1256 GIndex trIDs[3]{
1257 decay3Body.getProngID(0),
1258 decay3Body.getProngID(1),
1259 decay3Body.getProngID(2)};
1260 int tableIdx[3]{-1, -1, -1}, collID = -1;
1261 bool missing{false};
1262 for (int i{0}; i < 3; ++i) {
1263 auto item = mGIDToTableID.find(trIDs[i]);
1264 if (item != mGIDToTableID.end()) {
1265 tableIdx[i] = item->second;
1266 } else {
1267 LOG(warn) << fmt::format("Could not find a track index for prong ID {}", (int)trIDs[i]);
1268 missing = true;
1269 }
1270 }
1271 auto itemV = mVtxToTableCollID.find(decay3Body.getVertexID());
1272 if (itemV == mVtxToTableCollID.end()) {
1273 LOG(warn) << "Could not find 3 body collisionID for the vertex ID " << decay3Body.getVertexID();
1274 missing = true;
1275 } else {
1276 collID = itemV->second;
1277 }
1278 if (missing) {
1279 continue;
1280 }
1281 decay3BodyCursor(collID, tableIdx[0], tableIdx[1], tableIdx[2]);
1282 }
1283}
1284
1285template <typename FwdTrkClsCursorType>
1286void AODProducerWorkflowDPL::addClustersToFwdTrkClsTable(const o2::globaltracking::RecoContainer& recoData, FwdTrkClsCursorType& fwdTrkClsCursor, GIndex trackID, int fwdTrackId)
1287{
1288 const auto& mchTracks = recoData.getMCHTracks();
1289 const auto& mchmidMatches = recoData.getMCHMIDMatches();
1290 const auto& mchClusters = recoData.getMCHTrackClusters();
1291
1292 int mchTrackID = -1;
1293 if (trackID.getSource() == GIndex::MCH) { // This is an MCH track
1294 mchTrackID = trackID.getIndex();
1295 } else if (trackID.getSource() == GIndex::MCHMID) { // This is an MCH-MID track
1296 auto mchmidMatch = mchmidMatches[trackID.getIndex()];
1297 mchTrackID = mchmidMatch.getMCHRef().getIndex();
1298 } // Others are Global Forward Tracks, their clusters will be or were added with the corresponding MCH track
1299
1300 if (mchTrackID > -1 && mchTrackID < mchTracks.size()) {
1301 const auto& mchTrack = mchTracks[mchTrackID];
1302 fwdTrkClsCursor.reserve(mchTrack.getNClusters() + fwdTrkClsCursor.lastIndex());
1303 int first = mchTrack.getFirstClusterIdx();
1304 int last = mchTrack.getLastClusterIdx();
1305 for (int i = first; i <= last; i++) {
1306 const auto& cluster = mchClusters[i];
1307 fwdTrkClsCursor(fwdTrackId,
1308 truncateFloatFraction(cluster.x, mMuonCl),
1309 truncateFloatFraction(cluster.y, mMuonCl),
1310 truncateFloatFraction(cluster.z, mMuonCl),
1311 (((cluster.ey < 5.) & 0x1) << 12) | (((cluster.ex < 5.) & 0x1) << 11) | cluster.getDEId());
1312 }
1313 }
1314}
1315
1316template <typename HMPCursorType>
1317void AODProducerWorkflowDPL::fillHMPID(const o2::globaltracking::RecoContainer& recoData, HMPCursorType& hmpCursor)
1318{
1319 auto hmpMatches = recoData.getHMPMatches();
1320
1321 hmpCursor.reserve(hmpMatches.size());
1322
1323 // filling HMPs table
1324 for (size_t iHmp = 0; iHmp < hmpMatches.size(); iHmp++) {
1325
1326 const auto& match = hmpMatches[iHmp];
1327
1328 float xTrk, yTrk, theta, phi;
1329 float xMip, yMip;
1330 int charge, nph;
1331
1332 match.getHMPIDtrk(xTrk, yTrk, theta, phi);
1333 match.getHMPIDmip(xMip, yMip, charge, nph);
1334
1335 auto photChargeVec = match.getPhotCharge();
1336
1337 float photChargeVec2[10]; // = {0.,0.,0.,0.,0.,0.,0.,0.,0.,0.};
1338
1339 for (Int_t i = 0; i < 10; i++) {
1340 photChargeVec2[i] = photChargeVec[i];
1341 }
1342 auto tref = mGIDToTableID.find(match.getTrackRef());
1343 if (tref != mGIDToTableID.end()) {
1344 hmpCursor(tref->second, match.getHMPsignal(), xTrk, yTrk, xMip, yMip, nph, charge, match.getIdxHMPClus(), match.getHmpMom(), photChargeVec2);
1345 } else {
1346 LOG(error) << "Could not find AOD track table entry for HMP-matched track " << match.getTrackRef().asString();
1347 }
1348 }
1349}
1350
1351void AODProducerWorkflowDPL::prepareStrangenessTracking(const o2::globaltracking::RecoContainer& recoData)
1352{
1353 auto v0s = recoData.getV0sIdx();
1354 auto cascades = recoData.getCascadesIdx();
1355 auto decays3Body = recoData.getDecays3BodyIdx();
1356
1357 int sTrkID = 0;
1358 mCollisionStrTrk.clear();
1359 mCollisionStrTrk.reserve(recoData.getStrangeTracks().size());
1360 mVertexStrLUT.clear();
1361 mVertexStrLUT.resize(recoData.getPrimaryVertices().size() + 1, 0);
1362 for (auto& sTrk : recoData.getStrangeTracks()) {
1363 auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS};
1364 int vtxId{0};
1365 if (sTrk.mPartType == dataformats::kStrkV0) {
1366 vtxId = v0s[sTrk.mDecayRef].getVertexID();
1367 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1368 vtxId = cascades[sTrk.mDecayRef].getVertexID();
1369 } else {
1370 vtxId = decays3Body[sTrk.mDecayRef].getVertexID();
1371 }
1372 mCollisionStrTrk.emplace_back(vtxId, sTrkID++);
1373 mVertexStrLUT[vtxId]++;
1374 }
1375 std::exclusive_scan(mVertexStrLUT.begin(), mVertexStrLUT.end(), mVertexStrLUT.begin(), 0);
1376
1377 // sort by collision ID
1378 std::sort(mCollisionStrTrk.begin(), mCollisionStrTrk.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
1379 mStrTrkIndices.clear();
1380 mStrTrkIndices.resize(mCollisionStrTrk.size(), -1);
1381}
1382
1383template <typename V0C, typename CC, typename D3BC>
1384void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltracking::RecoContainer& recoData, V0C& v0Curs, CC& cascCurs, D3BC& d3BodyCurs)
1385{
1386 int itsTableIdx = -1;
1387 int sTrkID = 0;
1388 int nV0 = 0;
1389 int nCasc = 0;
1390 int nD3Body = 0;
1391
1392 for (const auto& sTrk : recoData.getStrangeTracks()) {
1393 if (sTrk.mPartType == dataformats::kStrkV0) {
1394 nV0++;
1395 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1396 nCasc++;
1397 } else {
1398 nD3Body++;
1399 }
1400 }
1401
1402 v0Curs.reserve(nV0);
1403 cascCurs.reserve(nCasc);
1404 d3BodyCurs.reserve(nD3Body);
1405
1406 for (const auto& sTrk : recoData.getStrangeTracks()) {
1407 auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS};
1408 auto item = mGIDToTableID.find(ITSIndex);
1409 if (item != mGIDToTableID.end()) {
1410 itsTableIdx = item->second;
1411 } else {
1412 LOG(warn) << "Could not find a ITS strange track index " << ITSIndex;
1413 continue;
1414 }
1415 if (sTrk.mPartType == dataformats::kStrkV0) {
1416 v0Curs(mStrTrkIndices[sTrkID++],
1417 itsTableIdx,
1418 sTrk.mDecayRef,
1419 sTrk.mDecayVtx[0],
1420 sTrk.mDecayVtx[1],
1421 sTrk.mDecayVtx[2],
1422 sTrk.mMasses[0],
1423 sTrk.mMasses[1],
1424 sTrk.mMatchChi2,
1425 sTrk.mTopoChi2,
1426 sTrk.getAverageClusterSize());
1427 } else if (sTrk.mPartType == dataformats::kStrkCascade) {
1428 cascCurs(mStrTrkIndices[sTrkID++],
1429 itsTableIdx,
1430 sTrk.mDecayRef,
1431 sTrk.mDecayVtx[0],
1432 sTrk.mDecayVtx[1],
1433 sTrk.mDecayVtx[2],
1434 sTrk.mMasses[0],
1435 sTrk.mMasses[1],
1436 sTrk.mMatchChi2,
1437 sTrk.mTopoChi2,
1438 sTrk.getAverageClusterSize());
1439 } else {
1440 d3BodyCurs(mStrTrkIndices[sTrkID++],
1441 itsTableIdx,
1442 sTrk.mDecayRef,
1443 sTrk.mDecayVtx[0],
1444 sTrk.mDecayVtx[1],
1445 sTrk.mDecayVtx[2],
1446 sTrk.mMasses[0],
1447 sTrk.mMasses[1],
1448 sTrk.mMatchChi2,
1449 sTrk.mTopoChi2,
1450 sTrk.getAverageClusterSize());
1451 }
1452 }
1453}
1454
1455void AODProducerWorkflowDPL::countTPCClusters(const o2::globaltracking::RecoContainer& data)
1456{
1457 const auto& tpcTracks = data.getTPCTracks();
1458 const auto& tpcClusRefs = data.getTPCTracksClusterRefs();
1459 const auto& tpcClusShMap = data.clusterShMapTPC;
1460 const auto& tpcClusAcc = data.getTPCClusters();
1461 constexpr int maxRows = 152;
1462 constexpr int neighbour = 2;
1463 int ntr = tpcTracks.size();
1464 mTPCCounters.clear();
1465 mTPCCounters.resize(ntr);
1466#ifdef WITH_OPENMP
1467 int ngroup = std::min(50, std::max(1, ntr / mNThreads));
1468#pragma omp parallel for schedule(dynamic, ngroup) num_threads(mNThreads)
1469#endif
1470 for (int itr = 0; itr < ntr; itr++) {
1471 std::array<bool, maxRows> clMap{}, shMap{};
1472 uint8_t sectorIndex, rowIndex;
1473 uint32_t clusterIndex;
1474 auto& counters = mTPCCounters[itr];
1475 const auto& track = tpcTracks[itr];
1476 for (int i = 0; i < track.getNClusterReferences(); i++) {
1477 o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef());
1478 unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex;
1479 clMap[rowIndex] = true;
1480 if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) {
1481 if (!shMap[rowIndex]) {
1482 counters.shared++;
1483 }
1484 shMap[rowIndex] = true;
1485 }
1486 }
1487 int last = -1;
1488 for (int i = 0; i < maxRows; i++) {
1489 if (clMap[i]) {
1490 counters.crossed++;
1491 counters.found++;
1492 last = i;
1493 } else if ((i - last) <= neighbour) {
1494 counters.crossed++;
1495 } else {
1496 int lim = std::min(i + 1 + neighbour, maxRows);
1497 for (int j = i + 1; j < lim; j++) {
1498 if (clMap[j]) {
1499 counters.crossed++;
1500 break;
1501 }
1502 }
1503 }
1504 }
1505 }
1506}
1507
1508uint8_t AODProducerWorkflowDPL::getTRDPattern(const o2::trd::TrackTRD& track)
1509{
1510 uint8_t pattern = 0;
1511 for (int il = o2::trd::TrackTRD::EGPUTRDTrack::kNLayers - 1; il >= 0; il--) {
1512 if (track.getTrackletIndex(il) != -1) {
1513 pattern |= 0x1 << il;
1514 }
1515 }
1516 if (track.getHasNeighbor()) {
1517 pattern |= 0x1 << 6;
1518 }
1519 if (track.getHasPadrowCrossing()) {
1520 pattern |= 0x1 << 7;
1521 }
1522 return pattern;
1523}
1524
1525template <typename TCaloHandler, typename TCaloCursor, typename TCaloTRGCursor, typename TMCCaloLabelCursor>
1526void AODProducerWorkflowDPL::addToCaloTable(TCaloHandler& caloHandler, TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor,
1527 TMCCaloLabelCursor& mcCaloCellLabelCursor, int eventID, int bcID, int8_t caloType)
1528{
1529 auto inputEvent = caloHandler.buildEvent(eventID);
1530 auto cellsInEvent = inputEvent.mCells; // get cells belonging to current event
1531 auto cellMClabels = inputEvent.mMCCellLabels; // get MC labels belonging to current event (only implemented for EMCal currently!)
1532 caloCellCursor.reserve(cellsInEvent.size() + caloCellCursor.lastIndex());
1533 caloTRGCursor.reserve(cellsInEvent.size() + caloTRGCursor.lastIndex());
1534 if (mUseMC) {
1535 mcCaloCellLabelCursor.reserve(cellsInEvent.size() + mcCaloCellLabelCursor.lastIndex());
1536 }
1537 for (auto iCell = 0U; iCell < cellsInEvent.size(); iCell++) {
1538 caloCellCursor(bcID,
1539 CellHelper::getCellNumber(cellsInEvent[iCell]),
1540 truncateFloatFraction(CellHelper::getAmplitude(cellsInEvent[iCell]), mCaloAmp),
1541 truncateFloatFraction(CellHelper::getTimeStamp(cellsInEvent[iCell]), mCaloTime),
1542 cellsInEvent[iCell].getType(),
1543 caloType); // 1 = emcal, -1 = undefined, 0 = phos
1544
1545 // todo: fix dummy values in CellHelper when it is clear what is filled for trigger information
1546 if (CellHelper::isTRU(cellsInEvent[iCell])) { // Write only trigger cells into this table
1547 caloTRGCursor(bcID,
1548 CellHelper::getFastOrAbsID(cellsInEvent[iCell]),
1549 CellHelper::getLnAmplitude(cellsInEvent[iCell]),
1550 CellHelper::getTriggerBits(cellsInEvent[iCell]),
1551 caloType);
1552 }
1553 if (mUseMC) {
1554 // Common for PHOS and EMCAL
1555 // loop over all MC Labels for the current cell
1556 std::vector<int32_t> particleIds;
1557 std::vector<float> amplitudeFraction;
1558 if (!mEMCselectLeading) {
1559 particleIds.reserve(cellMClabels.size());
1560 amplitudeFraction.reserve(cellMClabels.size());
1561 }
1562 float tmpMaxAmplitude = 0;
1563 int32_t tmpindex = 0;
1564 for (auto& mclabel : cellMClabels[iCell]) {
1565 // do not fill noise lables!
1566 if (mclabel.isValid()) {
1567 if (mEMCselectLeading) {
1568 if (mclabel.getAmplitudeFraction() > tmpMaxAmplitude) {
1569 // Check if this MCparticle added to be kept?
1570 if (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).find(mclabel.getTrackID()) !=
1571 mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).end()) {
1572 tmpMaxAmplitude = mclabel.getAmplitudeFraction();
1573 tmpindex = (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID())).at(mclabel.getTrackID());
1574 }
1575 }
1576 } else {
1577 auto trackStore = mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID());
1578 auto iter = trackStore.find(mclabel.getTrackID());
1579 if (iter != trackStore.end()) {
1580 amplitudeFraction.emplace_back(mclabel.getAmplitudeFraction());
1581 particleIds.emplace_back(iter->second);
1582 } else {
1583 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!
1584 amplitudeFraction.emplace_back(0.f);
1585 LOG(warn) << "CaloTable: Could not find track for mclabel (" << mclabel.getSourceID() << "," << mclabel.getEventID() << "," << mclabel.getTrackID() << ") in the AOD MC store";
1586 if (mMCKineReader) {
1587 auto mctrack = mMCKineReader->getTrack(mclabel);
1588 TVector3 vec;
1589 mctrack->GetStartVertex(vec);
1590 LOG(warn) << " ... this track is of PDG " << mctrack->GetPdgCode() << " produced by " << mctrack->getProdProcessAsString() << " at (" << vec.X() << "," << vec.Y() << "," << vec.Z() << ")";
1591 }
1592 }
1593 }
1594 }
1595 } // end of loop over all MC Labels for the current cell
1596 if (mEMCselectLeading) {
1597 amplitudeFraction.emplace_back(tmpMaxAmplitude);
1598 particleIds.emplace_back(tmpindex);
1599 }
1600 if (particleIds.size() == 0) {
1601 particleIds.emplace_back(-1);
1602 amplitudeFraction.emplace_back(0.f);
1603 }
1604 mcCaloCellLabelCursor(particleIds,
1605 amplitudeFraction);
1606 }
1607 } // end of loop over cells in current event
1608}
1609
1610// fill calo related tables (cells and calotrigger table)
1611template <typename TCaloCursor, typename TCaloTRGCursor, typename TMCCaloLabelCursor>
1612void AODProducerWorkflowDPL::fillCaloTable(TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor,
1613 TMCCaloLabelCursor& mcCaloCellLabelCursor, const std::map<uint64_t, int>& bcsMap,
1615{
1616 // get calo information
1617 auto caloEMCCells = data.getEMCALCells();
1618 auto caloEMCCellsTRGR = data.getEMCALTriggers();
1619 auto mcCaloEMCCellLabels = data.getEMCALCellsMCLabels();
1620
1621 auto caloPHOSCells = data.getPHOSCells();
1622 auto caloPHOSCellsTRGR = data.getPHOSTriggers();
1623 auto mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels();
1624
1625 if (!mInputSources[GIndex::PHS]) {
1626 caloPHOSCells = {};
1627 caloPHOSCellsTRGR = {};
1628 mcCaloPHOSCellLabels = {};
1629 }
1630
1631 if (!mInputSources[GIndex::EMC]) {
1632 caloEMCCells = {};
1633 caloEMCCellsTRGR = {};
1634 mcCaloEMCCellLabels = {};
1635 }
1636
1639
1640 // get cell belonging to an eveffillnt instead of timeframe
1641 emcEventHandler.reset();
1642 emcEventHandler.setCellData(caloEMCCells, caloEMCCellsTRGR);
1643 emcEventHandler.setCellMCTruthContainer(mcCaloEMCCellLabels);
1644
1645 phsEventHandler.reset();
1646 phsEventHandler.setCellData(caloPHOSCells, caloPHOSCellsTRGR);
1647 phsEventHandler.setCellMCTruthContainer(mcCaloPHOSCellLabels);
1648
1649 int emcNEvents = emcEventHandler.getNumberOfEvents();
1650 int phsNEvents = phsEventHandler.getNumberOfEvents();
1651
1652 std::vector<std::tuple<uint64_t, int8_t, int>> caloEvents; // <bc, caloType, eventID>
1653
1654 caloEvents.reserve(emcNEvents + phsNEvents);
1655
1656 for (int iev = 0; iev < emcNEvents; ++iev) {
1657 uint64_t bc = emcEventHandler.getInteractionRecordForEvent(iev).toLong();
1658 caloEvents.emplace_back(std::make_tuple(bc, 1, iev));
1659 }
1660
1661 for (int iev = 0; iev < phsNEvents; ++iev) {
1662 uint64_t bc = phsEventHandler.getInteractionRecordForEvent(iev).toLong();
1663 caloEvents.emplace_back(std::make_tuple(bc, 0, iev));
1664 }
1665
1666 std::sort(caloEvents.begin(), caloEvents.end(),
1667 [](const auto& left, const auto& right) { return std::get<0>(left) < std::get<0>(right); });
1668
1669 // loop over events
1670 for (int i = 0; i < emcNEvents + phsNEvents; ++i) {
1671 uint64_t globalBC = std::get<0>(caloEvents[i]);
1672 int8_t caloType = std::get<1>(caloEvents[i]);
1673 int eventID = std::get<2>(caloEvents[i]);
1674 auto item = bcsMap.find(globalBC);
1675 int bcID = -1;
1676 if (item != bcsMap.end()) {
1677 bcID = item->second;
1678 } else {
1679 LOG(warn) << "Error: could not find a corresponding BC ID for a calo point; globalBC = " << globalBC << ", caloType = " << (int)caloType;
1680 }
1681 if (caloType == 0) { // phos
1682 addToCaloTable(phsEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType);
1683 }
1684 if (caloType == 1) { // emc
1685 addToCaloTable(emcEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType);
1686 }
1687 }
1688
1689 caloEvents.clear();
1690}
1691
1693{
1694 mTimer.Stop();
1696 mLPMProdTag = ic.options().get<std::string>("lpmp-prod-tag");
1697 mAnchorPass = ic.options().get<std::string>("anchor-pass");
1698 mAnchorProd = ic.options().get<std::string>("anchor-prod");
1699 mUser = ic.options().get<std::string>("created-by");
1700 mRecoPass = ic.options().get<std::string>("reco-pass");
1701 mTFNumber = ic.options().get<int64_t>("aod-timeframe-id");
1702 mRecoOnly = ic.options().get<int>("reco-mctracks-only");
1703 mTruncate = ic.options().get<int>("enable-truncation");
1704 mRunNumber = ic.options().get<int>("run-number");
1705 mCTPReadout = ic.options().get<int>("ctpreadout-create");
1706 mNThreads = std::max(1, ic.options().get<int>("nthreads"));
1707 mEMCselectLeading = ic.options().get<bool>("emc-select-leading");
1708 mThinTracks = ic.options().get<bool>("thin-tracks");
1709 mPropTracks = ic.options().get<bool>("propagate-tracks");
1710 mMaxPropXiu = ic.options().get<float>("propagate-tracks-max-xiu");
1711 mPropMuons = ic.options().get<bool>("propagate-muons");
1712 if (auto s = ic.options().get<std::string>("with-streamers"); !s.empty()) {
1713 mStreamerFlags.set(s);
1714 if (mStreamerFlags) {
1715 LOGP(info, "Writing streamer data with mask:");
1716 LOG(info) << mStreamerFlags;
1717 } else {
1718 LOGP(warn, "Specified non-default empty streamer mask!");
1719 }
1720 }
1721 mTrackQCFraction = ic.options().get<float>("trackqc-fraction");
1722 mTrackQCNTrCut = ic.options().get<int64_t>("trackqc-NTrCut");
1723 mTrackQCDCAxy = ic.options().get<float>("trackqc-tpc-dca");
1724 mTrackQCPt = ic.options().get<float>("trackqc-tpc-pt");
1725 mTrackQCNCls = ic.options().get<int>("trackqc-tpc-cls");
1726 if (auto seed = ic.options().get<int>("seed"); seed == 0) {
1727 LOGP(info, "Using random device for seeding");
1728 std::random_device rd;
1729 std::array<int, std::mt19937::state_size> seed_data{};
1730 std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
1731 std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
1732 mGenerator = std::mt19937(seq);
1733 } else {
1734 LOGP(info, "Using seed {} for sampling", seed);
1735 mGenerator.seed(seed);
1736 }
1737#ifdef WITH_OPENMP
1738 LOGP(info, "Multi-threaded parts will run with {} OpenMP threads", mNThreads);
1739#else
1740 mNThreads = 1;
1741 LOG(info) << "OpenMP is disabled";
1742#endif
1743 if (mTFNumber == -1L) {
1744 LOG(info) << "TFNumber will be obtained from CCDB";
1745 }
1746 if (mRunNumber == -1L) {
1747 LOG(info) << "The Run number will be obtained from DPL headers";
1748 }
1749
1750 mUseSigFiltMC = ic.options().get<bool>("mc-signal-filt");
1751
1752 // set no truncation if selected by user
1753 if (mTruncate != 1) {
1754 LOG(info) << "Truncation is not used!";
1755 mCollisionPosition = 0xFFFFFFFF;
1756 mCollisionPositionCov = 0xFFFFFFFF;
1757 mTrackX = 0xFFFFFFFF;
1758 mTrackAlpha = 0xFFFFFFFF;
1759 mTrackSnp = 0xFFFFFFFF;
1760 mTrackTgl = 0xFFFFFFFF;
1761 mTrack1Pt = 0xFFFFFFFF;
1762 mTrackChi2 = 0xFFFFFFFF;
1763 mTrackCovDiag = 0xFFFFFFFF;
1764 mTrackCovOffDiag = 0xFFFFFFFF;
1765 mTrackSignal = 0xFFFFFFFF;
1766 mTrackTime = 0xFFFFFFFF;
1767 mTPCTime0 = 0xFFFFFFFF;
1768 mTrackTimeError = 0xFFFFFFFF;
1769 mTrackPosEMCAL = 0xFFFFFFFF;
1770 mTracklets = 0xFFFFFFFF;
1771 mMcParticleW = 0xFFFFFFFF;
1772 mMcParticlePos = 0xFFFFFFFF;
1773 mMcParticleMom = 0xFFFFFFFF;
1774 mCaloAmp = 0xFFFFFFFF;
1775 mCaloTime = 0xFFFFFFFF;
1776 mCPVPos = 0xFFFFFFFF;
1777 mCPVAmpl = 0xFFFFFFFF;
1778 mMuonTr1P = 0xFFFFFFFF;
1779 mMuonTrThetaX = 0xFFFFFFFF;
1780 mMuonTrThetaY = 0xFFFFFFFF;
1781 mMuonTrZmu = 0xFFFFFFFF;
1782 mMuonTrBend = 0xFFFFFFFF;
1783 mMuonTrNonBend = 0xFFFFFFFF;
1784 mMuonTrCov = 0xFFFFFFFF;
1785 mMuonCl = 0xFFFFFFFF;
1786 mMuonClErr = 0xFFFFFFFF;
1787 mV0Time = 0xFFFFFFFF;
1788 mV0ChannelTime = 0xFFFFFFFF;
1789 mFDDTime = 0xFFFFFFFF;
1790 mFDDChannelTime = 0xFFFFFFFF;
1791 mT0Time = 0xFFFFFFFF;
1792 mT0ChannelTime = 0xFFFFFFFF;
1793 mV0Amplitude = 0xFFFFFFFF;
1794 mFDDAmplitude = 0xFFFFFFFF;
1795 mT0Amplitude = 0xFFFFFFFF;
1796 }
1797 // Initialize ZDC helper maps
1798 for (int ic = 0; ic < o2::zdc::NChannels; ic++) {
1799 mZDCEnergyMap[ic] = -std::numeric_limits<float>::infinity();
1800 }
1801 for (int ic = 0; ic < o2::zdc::NTDCChannels; ic++) {
1802 mZDCTDCMap[ic] = -std::numeric_limits<float>::infinity();
1803 }
1804
1805 std::string hepmcUpdate = ic.options().get<std::string>("hepmc-update");
1806 HepMCUpdate when = (hepmcUpdate == "never" ? HepMCUpdate::never : hepmcUpdate == "always" ? HepMCUpdate::always
1807 : hepmcUpdate == "all" ? HepMCUpdate::allKeys
1808 : HepMCUpdate::anyKey);
1809 mXSectionUpdate = when;
1810 mPdfInfoUpdate = when;
1811 mHeavyIonUpdate = when;
1812
1813 mTimer.Reset();
1814
1815 if (mStreamerFlags) {
1816 mStreamer = std::make_unique<o2::utils::TreeStreamRedirector>("AO2DStreamer.root", "RECREATE");
1817 }
1818}
1819
1820namespace
1821{
1822void add_additional_meta_info(std::vector<TString>& keys, std::vector<TString>& values)
1823{
1824 // see if we should put additional meta info (e.g. from MC)
1825 auto aod_external_meta_info_file = getenv("AOD_ADDITIONAL_METADATA_FILE");
1826 if (aod_external_meta_info_file != nullptr) {
1827 LOG(info) << "Trying to inject additional AOD meta-data from " << aod_external_meta_info_file;
1828 if (std::filesystem::exists(aod_external_meta_info_file)) {
1829 std::ifstream input_file(aod_external_meta_info_file);
1830 if (input_file) {
1831 nlohmann::json json_data;
1832 try {
1833 input_file >> json_data;
1834 } catch (nlohmann::json::parse_error& e) {
1835 std::cerr << "JSON Parse Error: " << e.what() << "\n";
1836 std::cerr << "Exception ID: " << e.id << "\n";
1837 std::cerr << "Byte position: " << e.byte << "\n";
1838 return;
1839 }
1840 // If parsing succeeds, iterate over key-value pairs
1841 for (const auto& [key, value] : json_data.items()) {
1842 LOG(info) << "Adding AOD MetaData" << key << " : " << value;
1843 keys.push_back(key.c_str());
1844 values.push_back(value.get<std::string>());
1845 }
1846 }
1847 }
1848 }
1849}
1850} // namespace
1851
1853{
1854 mTimer.Start(false);
1856 recoData.collectData(pc, *mDataRequest);
1857 updateTimeDependentParams(pc); // Make sure that this is called after the RecoContainer collect data, since some condition objects are fetched there
1858
1859 mStartIR = recoData.startIR;
1860
1861 auto primVertices = recoData.getPrimaryVertices();
1862 auto primVer2TRefs = recoData.getPrimaryVertexMatchedTrackRefs();
1863 auto primVerGIs = recoData.getPrimaryVertexMatchedTracks();
1864 auto primVerLabels = recoData.getPrimaryVertexMCLabels();
1865
1866 auto fddChData = recoData.getFDDChannelsData();
1867 auto fddRecPoints = recoData.getFDDRecPoints();
1868 auto ft0ChData = recoData.getFT0ChannelsData();
1869 auto ft0RecPoints = recoData.getFT0RecPoints();
1870 auto fv0ChData = recoData.getFV0ChannelsData();
1871 auto fv0RecPoints = recoData.getFV0RecPoints();
1872
1873 auto zdcEnergies = recoData.getZDCEnergy();
1874 auto zdcBCRecData = recoData.getZDCBCRecData();
1875 auto zdcTDCData = recoData.getZDCTDCData();
1876
1877 auto cpvClusters = recoData.getCPVClusters();
1878 auto cpvTrigRecs = recoData.getCPVTriggers();
1879
1880 auto ctpDigits = recoData.getCTPDigits();
1881 const auto& tinfo = pc.services().get<o2::framework::TimingInfo>();
1882 std::vector<o2::ctp::CTPDigit> ctpDigitsCreated;
1883 if (mCTPReadout == 1) {
1884 LOG(info) << "CTP : creating ctpreadout in AOD producer";
1885 createCTPReadout(recoData, ctpDigitsCreated, pc);
1886 LOG(info) << "CTP : ctpreadout created from AOD";
1887 ctpDigits = gsl::span<o2::ctp::CTPDigit>(ctpDigitsCreated);
1888 }
1889 LOG(debug) << "FOUND " << primVertices.size() << " primary vertices";
1890 LOG(debug) << "FOUND " << ft0RecPoints.size() << " FT0 rec. points";
1891 LOG(debug) << "FOUND " << fv0RecPoints.size() << " FV0 rec. points";
1892 LOG(debug) << "FOUND " << fddRecPoints.size() << " FDD rec. points";
1893 LOG(debug) << "FOUND " << cpvClusters.size() << " CPV clusters";
1894 LOG(debug) << "FOUND " << cpvTrigRecs.size() << " CPV trigger records";
1895
1896 LOG(info) << "FOUND " << primVertices.size() << " primary vertices";
1897
1898 using namespace o2::aodhelpers;
1899
1900 auto bcCursor = createTableCursor<o2::aod::BCs>(pc);
1901 auto bcFlagsCursor = createTableCursor<o2::aod::BCFlags>(pc);
1902 auto cascadesCursor = createTableCursor<o2::aod::Cascades>(pc);
1903 auto collisionsCursor = createTableCursor<o2::aod::Collisions>(pc);
1904 auto decay3BodyCursor = createTableCursor<o2::aod::Decay3Bodys>(pc);
1905 auto trackedCascadeCursor = createTableCursor<o2::aod::TrackedCascades>(pc);
1906 auto trackedV0Cursor = createTableCursor<o2::aod::TrackedV0s>(pc);
1907 auto tracked3BodyCurs = createTableCursor<o2::aod::Tracked3Bodys>(pc);
1908 auto fddCursor = createTableCursor<o2::aod::FDDs>(pc);
1909 auto fddExtraCursor = createTableCursor<o2::aod::FDDsExtra>(pc);
1910 auto ft0Cursor = createTableCursor<o2::aod::FT0s>(pc);
1911 auto ft0ExtraCursor = createTableCursor<o2::aod::FT0sExtra>(pc);
1912 auto fv0aCursor = createTableCursor<o2::aod::FV0As>(pc);
1913 auto fv0aExtraCursor = createTableCursor<o2::aod::FV0AsExtra>(pc);
1914 auto fwdTracksCursor = createTableCursor<o2::aod::StoredFwdTracks>(pc);
1915 auto fwdTracksCovCursor = createTableCursor<o2::aod::StoredFwdTracksCov>(pc);
1916 auto fwdTrkClsCursor = createTableCursor<o2::aod::FwdTrkCls>(pc);
1917 auto mftTracksCursor = createTableCursor<o2::aod::StoredMFTTracks>(pc);
1918 auto mftTracksCovCursor = createTableCursor<o2::aod::StoredMFTTracksCov>(pc);
1919 auto tracksCursor = createTableCursor<o2::aod::StoredTracksIU>(pc);
1920 auto tracksCovCursor = createTableCursor<o2::aod::StoredTracksCovIU>(pc);
1921 auto tracksExtraCursor = createTableCursor<o2::aod::StoredTracksExtra>(pc);
1922 auto tracksQACursor = createTableCursor<o2::aod::TracksQAVersion>(pc);
1923 auto ambigTracksCursor = createTableCursor<o2::aod::AmbiguousTracks>(pc);
1924 auto ambigMFTTracksCursor = createTableCursor<o2::aod::AmbiguousMFTTracks>(pc);
1925 auto ambigFwdTracksCursor = createTableCursor<o2::aod::AmbiguousFwdTracks>(pc);
1926 auto v0sCursor = createTableCursor<o2::aod::V0s>(pc);
1927 auto zdcCursor = createTableCursor<o2::aod::Zdcs>(pc);
1928 auto hmpCursor = createTableCursor<o2::aod::HMPIDs>(pc);
1929 auto caloCellsCursor = createTableCursor<o2::aod::Calos>(pc);
1930 auto caloCellsTRGTableCursor = createTableCursor<o2::aod::CaloTriggers>(pc);
1931 auto cpvClustersCursor = createTableCursor<o2::aod::CPVClusters>(pc);
1932 auto originCursor = createTableCursor<o2::aod::Origins>(pc);
1933
1934 // Declare MC cursors type without adding the output for a table
1945 if (mUseMC) { // This creates the actual writercursor
1946 mcColLabelsCursor = createTableCursor<o2::aod::McCollisionLabels>(pc);
1947 mcCollisionsCursor = createTableCursor<o2::aod::McCollisions>(pc);
1948 hepmcXSectionsCursor = createTableCursor<o2::aod::HepMCXSections>(pc);
1949 hepmcPdfInfosCursor = createTableCursor<o2::aod::HepMCPdfInfos>(pc);
1950 hepmcHeavyIonsCursor = createTableCursor<o2::aod::HepMCHeavyIons>(pc);
1951 mcMFTTrackLabelCursor = createTableCursor<o2::aod::McMFTTrackLabels>(pc);
1952 mcFwdTrackLabelCursor = createTableCursor<o2::aod::McFwdTrackLabels>(pc);
1953 mcParticlesCursor = createTableCursor<o2::aod::StoredMcParticles_001>(pc);
1954 mcTrackLabelCursor = createTableCursor<o2::aod::McTrackLabels>(pc);
1955 mcCaloLabelsCursor = createTableCursor<o2::aod::McCaloLabels_001>(pc);
1956 }
1957
1958 std::unique_ptr<o2::steer::MCKinematicsReader> mcReader;
1959 if (mUseMC) {
1960 mcReader = std::make_unique<o2::steer::MCKinematicsReader>("collisioncontext.root");
1961 }
1962 mMCKineReader = mcReader.get(); // for use in different functions
1963 std::map<uint64_t, int> bcsMap;
1964 collectBCs(recoData, mUseMC ? mcReader->getDigitizationContext()->getEventRecords() : std::vector<o2::InteractionTimeRecord>{}, bcsMap);
1965 if (!primVer2TRefs.empty()) { // if the vertexing was done, the last slot refers to orphan tracks
1966 addRefGlobalBCsForTOF(primVer2TRefs.back(), primVerGIs, recoData, bcsMap);
1967 }
1968 // initialize the bunch crossing container for further use below
1969 mBCLookup.init(bcsMap);
1970
1971 uint64_t tfNumber;
1972 const int runNumber = (mRunNumber == -1) ? int(tinfo.runNumber) : mRunNumber;
1973 if (mTFNumber == -1L) {
1974 // TODO has to use absolute time of TF
1975 tfNumber = uint64_t(tinfo.firstTForbit) + (uint64_t(tinfo.runNumber) << 32); // getTFNumber(mStartIR, runNumber);
1976 } else {
1977 tfNumber = mTFNumber;
1978 }
1979
1980 std::vector<float> aAmplitudes, aTimes;
1981 std::vector<uint8_t> aChannels;
1982 fv0aCursor.reserve(fv0RecPoints.size());
1983 for (auto& fv0RecPoint : fv0RecPoints) {
1984 aAmplitudes.clear();
1985 aChannels.clear();
1986 aTimes.clear();
1987 const auto channelData = fv0RecPoint.getBunchChannelData(fv0ChData);
1988 for (auto& channel : channelData) {
1989 if (channel.charge > 0) {
1990 aAmplitudes.push_back(truncateFloatFraction(channel.charge, mV0Amplitude));
1991 aTimes.push_back(truncateFloatFraction(channel.time * 1.E-3, mV0ChannelTime));
1992 aChannels.push_back(channel.channel);
1993 }
1994 }
1995 uint64_t bc = fv0RecPoint.getInteractionRecord().toLong();
1996 auto item = bcsMap.find(bc);
1997 int bcID = -1;
1998 if (item != bcsMap.end()) {
1999 bcID = item->second;
2000 } else {
2001 LOG(fatal) << "Error: could not find a corresponding BC ID for a FV0 rec. point; BC = " << bc;
2002 }
2003 fv0aCursor(bcID,
2004 aAmplitudes,
2005 aChannels,
2006 truncateFloatFraction(fv0RecPoint.getCollisionGlobalMeanTime() * 1E-3, mV0Time), // ps to ns
2007 fv0RecPoint.getTrigger().getTriggersignals());
2008
2009 if (mEnableFITextra) {
2010 fv0aExtraCursor(bcID,
2011 aTimes);
2012 }
2013 }
2014
2015 std::vector<float> zdcEnergy, zdcAmplitudes, zdcTime;
2016 std::vector<uint8_t> zdcChannelsE, zdcChannelsT;
2017 zdcCursor.reserve(zdcBCRecData.size());
2018 for (auto zdcRecData : zdcBCRecData) {
2019 uint64_t bc = zdcRecData.ir.toLong();
2020 auto item = bcsMap.find(bc);
2021 int bcID = -1;
2022 if (item != bcsMap.end()) {
2023 bcID = item->second;
2024 } else {
2025 LOG(fatal) << "Error: could not find a corresponding BC ID for a ZDC rec. point; BC = " << bc;
2026 }
2027 int fe, ne, ft, nt, fi, ni;
2028 zdcRecData.getRef(fe, ne, ft, nt, fi, ni);
2029 zdcEnergy.clear();
2030 zdcChannelsE.clear();
2031 zdcAmplitudes.clear();
2032 zdcTime.clear();
2033 zdcChannelsT.clear();
2034 for (int ie = 0; ie < ne; ie++) {
2035 auto& zdcEnergyData = zdcEnergies[fe + ie];
2036 zdcEnergy.emplace_back(zdcEnergyData.energy());
2037 zdcChannelsE.emplace_back(zdcEnergyData.ch());
2038 }
2039 for (int it = 0; it < nt; it++) {
2040 auto& tdc = zdcTDCData[ft + it];
2041 zdcAmplitudes.emplace_back(tdc.amplitude());
2042 zdcTime.emplace_back(tdc.value());
2043 zdcChannelsT.emplace_back(o2::zdc::TDCSignal[tdc.ch()]);
2044 }
2045 zdcCursor(bcID,
2046 zdcEnergy,
2047 zdcChannelsE,
2048 zdcAmplitudes,
2049 zdcTime,
2050 zdcChannelsT);
2051 }
2052
2053 // keep track event/source id for each mc-collision
2054 // using map and not unordered_map to ensure
2055 // correct ordering when iterating over container elements
2056 std::vector<std::vector<int>> mcColToEvSrc;
2057
2058 if (mUseMC) {
2059 using namespace o2::aodmchelpers;
2060
2061 // filling mcCollision table
2062 int nMCCollisions = mcReader->getDigitizationContext()->getNCollisions();
2063 const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords();
2064 const auto& mcParts = mcReader->getDigitizationContext()->getEventParts();
2065
2066 // if signal filtering enabled, let's check if there are more than one source; otherwise fatalise
2067 if (mUseSigFiltMC) {
2068 std::vector<int> sourceIDs{};
2069 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
2070 for (auto const& colPart : mcParts[iCol]) {
2071 int sourceID = colPart.sourceID;
2072 if (std::find(sourceIDs.begin(), sourceIDs.end(), sourceID) == sourceIDs.end()) {
2073 sourceIDs.push_back(sourceID);
2074 }
2075 if (sourceIDs.size() > 1) { // we found more than one, exit
2076 break;
2077 }
2078 }
2079 if (sourceIDs.size() > 1) { // we found more than one, exit
2080 break;
2081 }
2082 }
2083 if (sourceIDs.size() <= 1) {
2084 LOGP(fatal, "Signal filtering cannot be enabled without embedding. Please fix the configuration either enabling the embedding, or turning off the signal filtering.");
2085 }
2086 }
2087
2088 // count all parts
2089 int totalNParts = 0;
2090 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
2091 totalNParts += mcParts[iCol].size();
2092 }
2093 mcCollisionsCursor.reserve(totalNParts);
2094
2095 for (int iCol = 0; iCol < nMCCollisions; iCol++) {
2096 const auto time = mcRecords[iCol].getTimeOffsetWrtBC();
2097 auto globalBC = mcRecords[iCol].toLong();
2098 auto item = bcsMap.find(globalBC);
2099 int bcID = -1;
2100 if (item != bcsMap.end()) {
2101 bcID = item->second;
2102 } else {
2103 LOG(fatal) << "Error: could not find a corresponding BC ID "
2104 << "for MC collision; BC = " << globalBC
2105 << ", mc collision = " << iCol;
2106 }
2107 auto& colParts = mcParts[iCol];
2108 auto nParts = colParts.size();
2109 for (auto colPart : colParts) {
2110 auto eventID = colPart.entryID;
2111 auto sourceID = colPart.sourceID;
2112 // enable embedding: if several colParts exist, then they are
2113 // saved as one collision
2114 if (nParts == 1 || sourceID == 0) {
2115 // FIXME:
2116 // use generators' names for generatorIDs (?)
2117 auto& header = mcReader->getMCEventHeader(sourceID, eventID);
2118 updateMCHeader(mcCollisionsCursor.cursor,
2119 hepmcXSectionsCursor.cursor,
2120 hepmcPdfInfosCursor.cursor,
2121 hepmcHeavyIonsCursor.cursor,
2122 header,
2123 iCol,
2124 bcID,
2125 time,
2126 0,
2127 sourceID);
2128 }
2129 mcColToEvSrc.emplace_back(std::vector<int>{iCol, sourceID, eventID}); // point background and injected signal events to one collision
2130 }
2131 }
2132 }
2133
2134 std::sort(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2135 [](const std::vector<int>& left, const std::vector<int>& right) { return (left[0] < right[0]); });
2136
2137 // vector of FDD amplitudes
2138 int16_t aFDDAmplitudesA[8] = {0u}, aFDDAmplitudesC[8] = {0u};
2139 float aFDDTimesA[8] = {0.f}, aFDDTimesC[8] = {0.f};
2140 // filling FDD table
2141 fddCursor.reserve(fddRecPoints.size());
2142 for (const auto& fddRecPoint : fddRecPoints) {
2143 for (int i = 0; i < 8; i++) {
2144 aFDDAmplitudesA[i] = 0;
2145 aFDDAmplitudesC[i] = 0;
2146 aFDDTimesA[i] = 0.f;
2147 aFDDTimesC[i] = 0.f;
2148 }
2149 uint64_t globalBC = fddRecPoint.getInteractionRecord().toLong();
2150 uint64_t bc = globalBC;
2151 auto item = bcsMap.find(bc);
2152 int bcID = -1;
2153 if (item != bcsMap.end()) {
2154 bcID = item->second;
2155 } else {
2156 LOG(fatal) << "Error: could not find a corresponding BC ID for a FDD rec. point; BC = " << bc;
2157 }
2158 const auto channelData = fddRecPoint.getBunchChannelData(fddChData);
2159 for (const auto& channel : channelData) {
2160 if (channel.mPMNumber < 8) {
2161 aFDDAmplitudesC[channel.mPMNumber] = channel.mChargeADC; // amplitude
2162 aFDDTimesC[channel.mPMNumber] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time
2163 } else {
2164 aFDDAmplitudesA[channel.mPMNumber - 8] = channel.mChargeADC; // amplitude
2165 aFDDTimesA[channel.mPMNumber - 8] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time
2166 }
2167 }
2168
2169 fddCursor(bcID,
2170 aFDDAmplitudesA,
2171 aFDDAmplitudesC,
2172 truncateFloatFraction(fddRecPoint.getCollisionTimeA() * 1E-3, mFDDTime), // ps to ns
2173 truncateFloatFraction(fddRecPoint.getCollisionTimeC() * 1E-3, mFDDTime), // ps to ns
2174 fddRecPoint.getTrigger().getTriggersignals());
2175 if (mEnableFITextra) {
2176 fddExtraCursor(bcID,
2177 aFDDTimesA,
2178 aFDDTimesC);
2179 }
2180 }
2181
2182 // filling FT0 table
2183 std::vector<float> aAmplitudesA, aAmplitudesC, aTimesA, aTimesC;
2184 std::vector<uint8_t> aChannelsA, aChannelsC;
2185 ft0Cursor.reserve(ft0RecPoints.size());
2186 for (auto& ft0RecPoint : ft0RecPoints) {
2187 aAmplitudesA.clear();
2188 aAmplitudesC.clear();
2189 aTimesA.clear();
2190 aTimesC.clear();
2191 aChannelsA.clear();
2192 aChannelsC.clear();
2193 const auto channelData = ft0RecPoint.getBunchChannelData(ft0ChData);
2194 for (auto& channel : channelData) {
2195 // TODO: switch to calibrated amplitude
2196 if (channel.QTCAmpl > 0) {
2197 constexpr int nFT0ChannelsAside = o2::ft0::Geometry::NCellsA * 4;
2198 if (channel.ChId < nFT0ChannelsAside) {
2199 aChannelsA.push_back(channel.ChId);
2200 aAmplitudesA.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude));
2201 aTimesA.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime));
2202 } else {
2203 aChannelsC.push_back(channel.ChId - nFT0ChannelsAside);
2204 aAmplitudesC.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude));
2205 aTimesC.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime));
2206 }
2207 }
2208 }
2209 uint64_t globalBC = ft0RecPoint.getInteractionRecord().toLong();
2210 uint64_t bc = globalBC;
2211 auto item = bcsMap.find(bc);
2212 int bcID = -1;
2213 if (item != bcsMap.end()) {
2214 bcID = item->second;
2215 } else {
2216 LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc;
2217 }
2218 ft0Cursor(bcID,
2219 aAmplitudesA,
2220 aChannelsA,
2221 aAmplitudesC,
2222 aChannelsC,
2223 truncateFloatFraction(ft0RecPoint.getCollisionTimeA() * 1E-3, mT0Time), // ps to ns
2224 truncateFloatFraction(ft0RecPoint.getCollisionTimeC() * 1E-3, mT0Time), // ps to ns
2225 ft0RecPoint.getTrigger().getTriggersignals());
2226 if (mEnableFITextra) {
2227 ft0ExtraCursor(bcID,
2228 aTimesA,
2229 aTimesC);
2230 }
2231 }
2232
2233 if (mUseMC) {
2234 // filling MC collision labels
2235 mcColLabelsCursor.reserve(primVerLabels.size());
2236 for (auto& label : primVerLabels) {
2237 auto it = std::find_if(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2238 [&label](const auto& mcColInfo) { return mcColInfo[1] == label.getSourceID() && mcColInfo[2] == label.getEventID(); });
2239 int32_t mcCollisionID = -1;
2240 if (it != mcColToEvSrc.end()) {
2241 mcCollisionID = it->at(0);
2242 }
2243 uint16_t mcMask = 0; // todo: set mask using normalized weights?
2244 mcColLabelsCursor(mcCollisionID, mcMask);
2245 }
2246 }
2247
2248 cacheTriggers(recoData);
2249 countTPCClusters(recoData);
2250
2251 int collisionID = 0;
2252 mIndexTableMFT.resize(recoData.getMFTTracks().size());
2253 mIndexTableFwd.resize(recoData.getMCHTracks().size());
2254
2255 auto& trackReffwd = primVer2TRefs.back();
2256 fillIndexTablesPerCollision(trackReffwd, primVerGIs, recoData);
2257 collisionID = 0;
2258 for (auto& vertex : primVertices) {
2259 auto& trackReffwd = primVer2TRefs[collisionID];
2260 fillIndexTablesPerCollision(trackReffwd, primVerGIs, recoData); // this function must follow the same track order as 'fillTrackTablesPerCollision' to fill the map of track indices
2261 collisionID++;
2262 }
2263
2265 prepareStrangenessTracking(recoData);
2266
2267 mGIDToTableFwdID.clear(); // reset the tables to be used by 'fillTrackTablesPerCollision'
2268 mGIDToTableMFTID.clear();
2269
2270 if (mPropTracks || mThinTracks) {
2271 auto v0s = recoData.getV0sIdx();
2272 auto cascades = recoData.getCascadesIdx();
2273 auto decays3Body = recoData.getDecays3BodyIdx();
2274 mGIDUsedBySVtx.reserve(v0s.size() * 2 + cascades.size() + decays3Body.size() * 3);
2275 for (const auto& v0 : v0s) {
2276 mGIDUsedBySVtx.insert(v0.getProngID(0));
2277 mGIDUsedBySVtx.insert(v0.getProngID(1));
2278 }
2279 for (const auto& cascade : cascades) {
2280 mGIDUsedBySVtx.insert(cascade.getBachelorID());
2281 }
2282 for (const auto& id3Body : decays3Body) {
2283 mGIDUsedBySVtx.insert(id3Body.getProngID(0));
2284 mGIDUsedBySVtx.insert(id3Body.getProngID(1));
2285 mGIDUsedBySVtx.insert(id3Body.getProngID(2));
2286 }
2287
2288 mGIDUsedByStr.reserve(recoData.getStrangeTracks().size());
2289 for (const auto& sTrk : recoData.getStrangeTracks()) {
2290 mGIDUsedByStr.emplace(sTrk.mITSRef, GIndex::ITS);
2291 }
2292 }
2293
2294 // filling unassigned tracks first
2295 // so that all unassigned tracks are stored in the beginning of the table together
2296 auto& trackRef = primVer2TRefs.back(); // references to unassigned tracks are at the end
2297 // fixme: interaction time is undefined for unassigned tracks (?)
2298 fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor,
2299 ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor,
2300 fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap);
2301
2302 // filling collisions and tracks into tables
2303 collisionID = 0;
2304 collisionsCursor.reserve(primVertices.size());
2305 for (auto& vertex : primVertices) {
2306 auto& cov = vertex.getCov();
2307 auto& timeStamp = vertex.getTimeStamp(); // this is a relative time
2308 const double interactionTime = timeStamp.getTimeStamp() * 1E3; // mus to ns
2309 uint64_t globalBC = relativeTime_to_GlobalBC(interactionTime);
2310 uint64_t localBC = relativeTime_to_LocalBC(interactionTime);
2311 LOG(debug) << "global BC " << globalBC << " local BC " << localBC << " relative interaction time " << interactionTime;
2312 // collision timestamp in ns wrt the beginning of collision BC
2313 const float relInteractionTime = static_cast<float>(localBC * o2::constants::lhc::LHCBunchSpacingNS - interactionTime);
2314 auto item = bcsMap.find(globalBC);
2315 int bcID = -1;
2316 if (item != bcsMap.end()) {
2317 bcID = item->second;
2318 } else {
2319 LOG(fatal) << "Error: could not find a corresponding BC ID for a collision; BC = " << globalBC << ", collisionID = " << collisionID;
2320 }
2321 collisionsCursor(bcID,
2322 truncateFloatFraction(vertex.getX(), mCollisionPosition),
2323 truncateFloatFraction(vertex.getY(), mCollisionPosition),
2324 truncateFloatFraction(vertex.getZ(), mCollisionPosition),
2325 truncateFloatFraction(cov[0], mCollisionPositionCov),
2326 truncateFloatFraction(cov[1], mCollisionPositionCov),
2327 truncateFloatFraction(cov[2], mCollisionPositionCov),
2328 truncateFloatFraction(cov[3], mCollisionPositionCov),
2329 truncateFloatFraction(cov[4], mCollisionPositionCov),
2330 truncateFloatFraction(cov[5], mCollisionPositionCov),
2331 vertex.getFlags(),
2332 truncateFloatFraction(vertex.getChi2(), mCollisionPositionCov),
2333 vertex.getNContributors(),
2334 truncateFloatFraction(relInteractionTime, mCollisionPosition),
2335 truncateFloatFraction(timeStamp.getTimeStampError() * 1E3, mCollisionPositionCov));
2336 mVtxToTableCollID[collisionID] = mTableCollID++;
2337
2338 auto& trackRef = primVer2TRefs[collisionID];
2339 // passing interaction time in [ps]
2340 fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, ambigTracksCursor,
2341 mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor,
2342 fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap);
2343 collisionID++;
2344 }
2345
2346 fillSecondaryVertices(recoData, v0sCursor, cascadesCursor, decay3BodyCursor);
2347 fillHMPID(recoData, hmpCursor);
2348 fillStrangenessTrackingTables(recoData, trackedV0Cursor, trackedCascadeCursor, tracked3BodyCurs);
2349
2350 // helper map for fast search of a corresponding class mask for a bc
2351 auto emcalIncomplete = filterEMCALIncomplete(recoData.getEMCALTriggers());
2352 std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> bcToClassMask;
2353 if (mInputSources[GID::CTP]) {
2354 LOG(debug) << "CTP input available";
2355 for (auto& ctpDigit : ctpDigits) {
2356 uint64_t bc = ctpDigit.intRecord.toLong();
2357 uint64_t classMask = ctpDigit.CTPClassMask.to_ulong();
2358 uint64_t inputMask = ctpDigit.CTPInputMask.to_ulong();
2359 if (emcalIncomplete.find(bc) != emcalIncomplete.end()) {
2360 // reject EMCAL triggers as BC was rejected as incomplete at readout level
2361 auto classMaskOrig = classMask;
2362 classMask = classMask & ~mEMCALTrgClassMask;
2363 LOG(debug) << "Found EMCAL incomplete event, mask before " << std::bitset<64>(classMaskOrig) << ", after " << std::bitset<64>(classMask);
2364 }
2365 bcToClassMask[bc] = {classMask, inputMask};
2366 // LOG(debug) << Form("classmask:0x%llx", classMask);
2367 }
2368 }
2369
2370 // filling BC table
2371 bcCursor.reserve(bcsMap.size());
2372 for (auto& item : bcsMap) {
2373 uint64_t bc = item.first;
2374 std::pair<uint64_t, uint64_t> masks{0, 0};
2375 if (mInputSources[GID::CTP]) {
2376 auto bcClassPair = bcToClassMask.find(bc);
2377 if (bcClassPair != bcToClassMask.end()) {
2378 masks = bcClassPair->second;
2379 }
2380 }
2381 bcCursor(runNumber,
2382 bc,
2383 masks.first,
2384 masks.second);
2385 }
2386
2387 bcToClassMask.clear();
2388
2389 // filling BC flags table:
2390 auto bcFlags = fillBCFlags(recoData, bcsMap);
2391 bcFlagsCursor.reserve(bcFlags.size());
2392 for (auto f : bcFlags) {
2393 bcFlagsCursor(f);
2394 }
2395
2396 // fill cpvcluster table
2397 if (mInputSources[GIndex::CPV]) {
2398 float posX, posZ;
2399 cpvClustersCursor.reserve(cpvTrigRecs.size());
2400 for (auto& cpvEvent : cpvTrigRecs) {
2401 uint64_t bc = cpvEvent.getBCData().toLong();
2402 auto item = bcsMap.find(bc);
2403 int bcID = -1;
2404 if (item != bcsMap.end()) {
2405 bcID = item->second;
2406 } else {
2407 LOG(fatal) << "Error: could not find a corresponding BC ID for a CPV Trigger Record; BC = " << bc;
2408 }
2409 for (int iClu = cpvEvent.getFirstEntry(); iClu < cpvEvent.getFirstEntry() + cpvEvent.getNumberOfObjects(); iClu++) {
2410 auto& clu = cpvClusters[iClu];
2411 clu.getLocalPosition(posX, posZ);
2412 cpvClustersCursor(bcID,
2413 truncateFloatFraction(posX, mCPVPos),
2414 truncateFloatFraction(posZ, mCPVPos),
2415 truncateFloatFraction(clu.getEnergy(), mCPVAmpl),
2416 clu.getPackedClusterStatus());
2417 }
2418 }
2419 }
2420
2421 if (mUseMC) {
2422 TStopwatch timer;
2423 timer.Start();
2424 // filling mc particles table
2425 fillMCParticlesTable(*mcReader,
2426 mcParticlesCursor.cursor,
2427 primVer2TRefs,
2428 primVerGIs,
2429 recoData,
2430 mcColToEvSrc);
2431 timer.Stop();
2432 LOG(info) << "FILL MC took " << timer.RealTime() << " s";
2433 mcColToEvSrc.clear();
2434
2435 // ------------------------------------------------------
2436 // filling track labels
2437
2438 // need to go through labels in the same order as for tracks
2439 fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, primVer2TRefs.back(), primVerGIs, recoData);
2440 for (auto iref = 0U; iref < primVer2TRefs.size() - 1; iref++) {
2441 auto& trackRef = primVer2TRefs[iref];
2442 fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, trackRef, primVerGIs, recoData, iref);
2443 }
2444 }
2445
2446 // Fill calo tables and if MC also the MCCaloTable, therefore, has to be after fillMCParticlesTable call!
2447 if (mInputSources[GIndex::PHS] || mInputSources[GIndex::EMC]) {
2448 fillCaloTable(caloCellsCursor, caloCellsTRGTableCursor, mcCaloLabelsCursor, bcsMap, recoData);
2449 }
2450
2451 bcsMap.clear();
2452 clearMCKeepStore(mToStore);
2453 mGIDToTableID.clear();
2454 mTableTrID = 0;
2455 mGIDToTableFwdID.clear();
2456 mTableTrFwdID = 0;
2457 mGIDToTableMFTID.clear();
2458 mTableTrMFTID = 0;
2459 mVtxToTableCollID.clear();
2460 mTableCollID = 0;
2461 mV0ToTableID.clear();
2462 mTableV0ID = 0;
2463
2464 mIndexTableFwd.clear();
2465 mIndexFwdID = 0;
2466 mIndexTableMFT.clear();
2467 mIndexMFTID = 0;
2468
2469 mBCLookup.clear();
2470
2471 mGIDUsedBySVtx.clear();
2472 mGIDUsedByStr.clear();
2473
2474 originCursor(tfNumber);
2475
2476 // sending metadata to writer
2477 TString dataType = mUseMC ? "MC" : "RAW";
2478 TString O2Version = o2::fullVersion();
2479 TString ROOTVersion = ROOT_RELEASE;
2480 mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag", "CreatedBy"};
2481 mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag, mUser};
2482 add_additional_meta_info(mMetaDataKeys, mMetaDataVals);
2483
2484 pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, mMetaDataKeys);
2485 pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, mMetaDataVals);
2486
2487 pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber);
2488 pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, "");
2489
2490 mTimer.Stop();
2491}
2492
2493void AODProducerWorkflowDPL::cacheTriggers(const o2::globaltracking::RecoContainer& recoData)
2494{
2495 // ITS tracks->ROF
2496 {
2497 mITSROFs.clear();
2498 const auto& rofs = recoData.getITSTracksROFRecords();
2499 uint16_t count = 0;
2500 for (const auto& rof : rofs) {
2501 int first = rof.getFirstEntry(), last = first + rof.getNEntries();
2502 for (int i = first; i < last; i++) {
2503 mITSROFs.push_back(count);
2504 }
2505 count++;
2506 }
2507 }
2508 // MFT tracks->ROF
2509 {
2510 mMFTROFs.clear();
2511 const auto& rofs = recoData.getMFTTracksROFRecords();
2512 uint16_t count = 0;
2513 for (const auto& rof : rofs) {
2514 int first = rof.getFirstEntry(), last = first + rof.getNEntries();
2515 for (int i = first; i < last; i++) {
2516 mMFTROFs.push_back(count);
2517 }
2518 count++;
2519 }
2520 }
2521 // ITSTPCTRD tracks -> TRD trigger
2522 {
2523 mITSTPCTRDTriggers.clear();
2524 const auto& itstpctrigs = recoData.getITSTPCTRDTriggers();
2525 int count = 0;
2526 for (const auto& trig : itstpctrigs) {
2527 int first = trig.getFirstTrack(), last = first + trig.getNumberOfTracks();
2528 for (int i = first; i < last; i++) {
2529 mITSTPCTRDTriggers.push_back(count);
2530 }
2531 count++;
2532 }
2533 }
2534 // TPCTRD tracks -> TRD trigger
2535 {
2536 mTPCTRDTriggers.clear();
2537 const auto& tpctrigs = recoData.getTPCTRDTriggers();
2538 int count = 0;
2539 for (const auto& trig : tpctrigs) {
2540 int first = trig.getFirstTrack(), last = first + trig.getNumberOfTracks();
2541 for (int i = first; i < last; i++) {
2542 mTPCTRDTriggers.push_back(count);
2543 }
2544 count++;
2545 }
2546 }
2547 // MCH tracks->ROF
2548 {
2549 mMCHROFs.clear();
2550 const auto& rofs = recoData.getMCHTracksROFRecords();
2551 uint16_t count = 0;
2552 for (const auto& rof : rofs) {
2553 int first = rof.getFirstIdx(), last = first + rof.getNEntries();
2554 for (int i = first; i < last; i++) {
2555 mMCHROFs.push_back(count);
2556 }
2557 count++;
2558 }
2559 }
2560}
2561
2562AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrack(int collisionID, std::uint64_t collisionBC, GIndex trackIndex,
2563 const o2::globaltracking::RecoContainer& data, const std::map<uint64_t, int>& bcsMap)
2564{
2565 TrackExtraInfo extraInfoHolder;
2566 if (collisionID < 0) {
2567 extraInfoHolder.flags |= o2::aod::track::OrphanTrack;
2568 }
2569 bool needBCSlice = collisionID < 0; // track is associated to multiple vertices
2570 uint64_t bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default track time is wrt collision BC (unless no collision assigned)
2571
2572 auto setTrackTime = [&](double t, double terr, bool gaussian) {
2573 // set track time and error, for ambiguous tracks define the bcSlice as it was used in vertex-track association
2574 // 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
2575 if (!gaussian) {
2576 extraInfoHolder.flags |= o2::aod::track::TrackTimeResIsRange;
2577 }
2578 extraInfoHolder.trackTimeRes = terr;
2579 if (needBCSlice) { // need to define BC slice
2580 double error = this->mTimeMarginTrackTime + (gaussian ? extraInfoHolder.trackTimeRes * this->mNSigmaTimeTrack : extraInfoHolder.trackTimeRes);
2581 bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - error, t + error, bcsMap);
2582 }
2583 extraInfoHolder.trackTime = float(t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS);
2584 extraInfoHolder.diffBCRef = int(bcOfTimeRef);
2585 LOGP(debug, "time : {}/{} -> {}/{} -> trunc: {}/{} CollID: {} Amb: {}", t, terr, t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS, terr,
2586 truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime), truncateFloatFraction(extraInfoHolder.trackTimeRes, mTrackTimeError),
2587 collisionID, trackIndex.isAmbiguous());
2588 };
2589 auto contributorsGID = data.getSingleDetectorRefs(trackIndex);
2590 const auto& trackPar = data.getTrackParam(trackIndex);
2591 extraInfoHolder.flags |= trackPar.getPID() << 28;
2592 auto src = trackIndex.getSource();
2593 if (contributorsGID[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF
2594 const auto& tofMatch = data.getTOFMatch(trackIndex);
2595 extraInfoHolder.tofChi2 = tofMatch.getChi2();
2596 const auto& tofInt = tofMatch.getLTIntegralOut();
2597 float intLen = tofInt.getL();
2598 extraInfoHolder.length = intLen;
2599 const float mass = o2::constants::physics::MassPionCharged; // default pid = pion
2600 if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) {
2601 float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed));
2602 if (expBeta > o2::constants::math::Almost1) {
2604 }
2605 extraInfoHolder.tofExpMom = mass * expBeta / std::sqrt(1.f - expBeta * expBeta);
2606 }
2607 // correct the time of the track
2608 const double massZ = o2::track::PID::getMass2Z(trackPar.getPID());
2609 const double energy = sqrt((massZ * massZ) + (extraInfoHolder.tofExpMom * extraInfoHolder.tofExpMom));
2610 const double exp = extraInfoHolder.length * energy / (cSpeed * extraInfoHolder.tofExpMom);
2611 auto tofSignal = (tofMatch.getSignal() - exp) * 1e-3; // time in ns wrt TF start
2612 setTrackTime(tofSignal, 0.2, true); // FIXME: calculate actual resolution (if possible?)
2613 }
2614 if (contributorsGID[GIndex::Source::TRD].isIndexSet()) { // ITS-TPC-TRD-TOF, TPC-TRD-TOF, TPC-TRD, ITS-TPC-TRD
2615 const auto& trdOrig = data.getTrack<o2::trd::TrackTRD>(contributorsGID[GIndex::Source::TRD]); // refitted TRD trac
2616 extraInfoHolder.trdChi2 = trdOrig.getChi2();
2617 extraInfoHolder.trdSignal = trdOrig.getSignal();
2618 extraInfoHolder.trdPattern = getTRDPattern(trdOrig);
2619 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
2620 // TRD is triggered: time uncertainty is within a BC
2621 const auto& trdTrig = (src == GIndex::Source::TPCTRD) ? data.getTPCTRDTriggers()[mTPCTRDTriggers[trackIndex.getIndex()]] : data.getITSTPCTRDTriggers()[mITSTPCTRDTriggers[trackIndex.getIndex()]];
2622 double ttrig = trdTrig.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS; // 1st get time wrt TF start
2623 setTrackTime(ttrig, 1., true); // FIXME: calculate actual resolution (if possible?)
2624 }
2625 }
2626 if (contributorsGID[GIndex::Source::ITS].isIndexSet()) {
2627 const auto& itsTrack = data.getITSTrack(contributorsGID[GIndex::ITS]);
2628 int nClusters = itsTrack.getNClusters();
2629 float chi2 = itsTrack.getChi2();
2630 extraInfoHolder.itsChi2NCl = nClusters != 0 ? chi2 / (float)nClusters : 0;
2631 extraInfoHolder.itsClusterSizes = itsTrack.getClusterSizes();
2632 if (src == GIndex::ITS) { // standalone ITS track should set its time from the ROF
2633 const auto& rof = data.getITSTracksROFRecords()[mITSROFs[trackIndex.getIndex()]];
2634 double t = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mITSROFrameHalfLengthNS + mITSROFBiasNS;
2635 setTrackTime(t, mITSROFrameHalfLengthNS, false);
2636 }
2637 } else if (contributorsGID[GIndex::Source::ITSAB].isIndexSet()) { // this is an ITS-TPC afterburner contributor
2638 extraInfoHolder.itsClusterSizes = data.getITSABRefs()[contributorsGID[GIndex::Source::ITSAB].getIndex()].getClusterSizes();
2639 }
2640 if (contributorsGID[GIndex::Source::TPC].isIndexSet()) {
2641 const auto& tpcOrig = data.getTPCTrack(contributorsGID[GIndex::TPC]);
2642 const auto& tpcClData = mTPCCounters[contributorsGID[GIndex::TPC]];
2643 const auto& dEdx = tpcOrig.getdEdx().dEdxTotTPC > 0 ? tpcOrig.getdEdx() : tpcOrig.getdEdxAlt();
2644 if (tpcOrig.getdEdx().dEdxTotTPC == 0) {
2645 extraInfoHolder.flags |= o2::aod::track::TPCdEdxAlt;
2646 }
2647 extraInfoHolder.tpcInnerParam = tpcOrig.getP() / tpcOrig.getAbsCharge();
2648 extraInfoHolder.tpcChi2NCl = tpcOrig.getNClusters() ? tpcOrig.getChi2() / tpcOrig.getNClusters() : 0;
2649 extraInfoHolder.tpcSignal = dEdx.dEdxTotTPC;
2650 extraInfoHolder.tpcNClsFindable = tpcOrig.getNClusters();
2651 extraInfoHolder.tpcNClsFindableMinusFound = tpcOrig.getNClusters() - tpcClData.found;
2652 extraInfoHolder.tpcNClsFindableMinusCrossedRows = tpcOrig.getNClusters() - tpcClData.crossed;
2653 extraInfoHolder.tpcNClsShared = tpcClData.shared;
2654 uint32_t clsUsedForPID = dEdx.NHitsIROC + dEdx.NHitsOROC1 + dEdx.NHitsOROC2 + dEdx.NHitsOROC3;
2655 extraInfoHolder.tpcNClsFindableMinusPID = tpcOrig.getNClusters() - clsUsedForPID;
2656 if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range
2657 if (needBCSlice) {
2658 double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value
2659 double terr = 0.5 * (tpcOrig.getDeltaTFwd() + tpcOrig.getDeltaTBwd()) * mTPCBinNS;
2660 double err = mTimeMarginTrackTime + terr;
2661 bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - err, t + err, bcsMap);
2662 }
2664 p.setDeltaTFwd(tpcOrig.getDeltaTFwd());
2665 p.setDeltaTBwd(tpcOrig.getDeltaTBwd());
2666 extraInfoHolder.trackTimeRes = p.getTimeErr();
2667 extraInfoHolder.trackTime = float(tpcOrig.getTime0() * mTPCBinNS - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS);
2668 extraInfoHolder.diffBCRef = int(bcOfTimeRef);
2669 extraInfoHolder.isTPConly = true; // no truncation
2670 extraInfoHolder.flags |= o2::aod::track::TrackTimeAsym;
2671 } else if (src == GIndex::ITSTPC) { // its-tpc matched tracks have gaussian time error and the time was not set above
2672 const auto& trITSTPC = data.getTPCITSTrack(trackIndex);
2673 auto ts = trITSTPC.getTimeMUS();
2674 setTrackTime(ts.getTimeStamp() * 1.e3, ts.getTimeStampError() * 1.e3, true);
2675 }
2676 }
2677
2678 extrapolateToCalorimeters(extraInfoHolder, data.getTrackParamOut(trackIndex));
2679 // set bit encoding for PVContributor property as part of the flag field
2680 if (trackIndex.isPVContributor()) {
2681 extraInfoHolder.flags |= o2::aod::track::PVContributor;
2682 }
2683 return extraInfoHolder;
2684}
2685
2686AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int collisionID, std::uint64_t collisionBC, GIndex trackIndex,
2687 const o2::globaltracking::RecoContainer& data, const std::map<uint64_t, int>& bcsMap)
2688{
2689 TrackQA trackQAHolder;
2690 auto contributorsGID = data.getTPCContributorGID(trackIndex);
2691 const auto& trackPar = data.getTrackParam(trackIndex);
2692 if (contributorsGID.isIndexSet()) {
2693 auto prop = o2::base::Propagator::Instance();
2694 const auto& tpcOrig = data.getTPCTrack(contributorsGID);
2697 const o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT;
2698 const o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ());
2699 std::array<float, 2> dcaInfo{-999., -999.};
2700 if (prop->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) {
2701 trackQAHolder.tpcdcaR = 100. * dcaInfo[0] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt());
2702 trackQAHolder.tpcdcaZ = 100. * dcaInfo[1] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt());
2703 }
2704 // This allows to safely clamp any float to one byte, using the
2705 // minmal/maximum values as under-/overflow borders and rounding to the nearest integer
2706 auto safeInt8Clamp = [](auto value) -> int8_t {
2707 using ValType = decltype(value);
2708 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()))));
2709 };
2710 auto safeUInt8Clamp = [](auto value) -> uint8_t {
2711 using ValType = decltype(value);
2712 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()))));
2713 };
2714
2716 uint8_t clusterCounters[8] = {0};
2717 {
2718 uint8_t sectorIndex, rowIndex;
2719 uint32_t clusterIndex;
2720 const auto& tpcClusRefs = data.getTPCTracksClusterRefs();
2721 for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) {
2722 o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, tpcOrig.getClusterRef());
2723 char indexTracklet = (rowIndex % 152) / 19;
2724 clusterCounters[indexTracklet]++;
2725 }
2726 }
2727 uint8_t byteMask = 0;
2728 for (int i = 0; i < 8; i++) {
2729 if (clusterCounters[i] > 5) {
2730 byteMask |= (1 << i);
2731 }
2732 }
2733 trackQAHolder.tpcTime0 = tpcOrig.getTime0();
2734 trackQAHolder.tpcClusterByteMask = byteMask;
2735 const auto& dEdxInfoAlt = tpcOrig.getdEdxAlt(); // tpcOrig.getdEdx()
2736 const float dEdxNorm = (dEdxInfoAlt.dEdxTotTPC > 0) ? 100. / dEdxInfoAlt.dEdxTotTPC : 0;
2737 trackQAHolder.tpcdEdxNorm = dEdxInfoAlt.dEdxTotTPC;
2738 trackQAHolder.tpcdEdxMax0R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxIROC * dEdxNorm);
2739 trackQAHolder.tpcdEdxMax1R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC1 * dEdxNorm);
2740 trackQAHolder.tpcdEdxMax2R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC2 * dEdxNorm);
2741 trackQAHolder.tpcdEdxMax3R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC3 * dEdxNorm);
2742 //
2743 trackQAHolder.tpcdEdxTot0R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotIROC * dEdxNorm);
2744 trackQAHolder.tpcdEdxTot1R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC1 * dEdxNorm);
2745 trackQAHolder.tpcdEdxTot2R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC2 * dEdxNorm);
2746 trackQAHolder.tpcdEdxTot3R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC3 * dEdxNorm);
2748 float scaleTOF{0};
2749 auto contributorsGIDA = data.getSingleDetectorRefs(trackIndex);
2750 if (contributorsGIDA[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF
2751 const auto& tofMatch = data.getTOFMatch(trackIndex);
2752 const float qpt = trackPar.getQ2Pt();
2754 trackQAHolder.dTofdX = safeInt8Clamp(tofMatch.getDXatTOF() / scaleTOF);
2755 trackQAHolder.dTofdZ = safeInt8Clamp(tofMatch.getDZatTOF() / scaleTOF);
2756 }
2757
2758 // Add matching information at a reference point (defined by
2759 // o2::aod::track::trackQARefRadius) in the same frame as the global track
2760 // without material corrections and error propagation
2761 if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) {
2762 const auto& itsOrig = data.getITSTrack(itsContGID);
2763 o2::track::TrackPar gloCopy = trackPar;
2764 o2::track::TrackPar itsCopy = itsOrig.getParamOut();
2765 o2::track::TrackPar tpcCopy = tpcOrig;
2766 if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) &&
2767 prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) &&
2768 prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) {
2769 // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track
2770 // The scale is defined by the global track scaling depending on beta0
2771 const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f));
2772 const float qpt = gloCopy.getQ2Pt();
2773 const float x = qpt / beta0;
2774 // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2)
2775 auto scaleCont = [&x](int i) -> float {
2777 };
2778 auto scaleGlo = [&x](int i) -> float {
2780 };
2781
2782 // Calculate deltas for contributors
2783 trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0));
2784 trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1));
2785 trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2));
2786 trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3));
2787 trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4));
2788 // Calculate deltas for global track against averaged contributors
2789 trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0));
2790 trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1));
2791 trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2));
2792 trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3));
2793 trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4));
2794 //
2795
2796 if (mStreamerFlags[AODProducerStreamerFlags::TrackQA]) {
2797 (*mStreamer) << "trackQA"
2798 << "trackITSOrig=" << itsOrig
2799 << "trackTPCOrig=" << tpcOrig
2800 << "trackITSTPCOrig=" << trackPar
2801 << "trackITSProp=" << itsCopy
2802 << "trackTPCProp=" << tpcCopy
2803 << "trackITSTPCProp=" << gloCopy
2804 << "refRadius=" << o2::aod::track::trackQARefRadius
2805 << "scaleBins=" << o2::aod::track::trackQAScaleBins
2806 << "scaleCont0=" << scaleCont(0)
2807 << "scaleCont1=" << scaleCont(1)
2808 << "scaleCont2=" << scaleCont(2)
2809 << "scaleCont3=" << scaleCont(3)
2810 << "scaleCont4=" << scaleCont(4)
2811 << "scaleGlo0=" << scaleGlo(0)
2812 << "scaleGlo1=" << scaleGlo(1)
2813 << "scaleGlo2=" << scaleGlo(2)
2814 << "scaleGlo3=" << scaleGlo(3)
2815 << "scaleGlo4=" << scaleGlo(4)
2816 << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0
2817 << "trackQAHolder.tpcdEdxNorm=" << trackQAHolder.tpcdEdxNorm
2818 << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR
2819 << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ
2820 << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask
2821 << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R
2822 << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R
2823 << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R
2824 << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R
2825 << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R
2826 << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R
2827 << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R
2828 << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R
2829 << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY
2830 << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ
2831 << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp
2832 << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl
2833 << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt
2834 << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY
2835 << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ
2836 << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp
2837 << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl
2838 << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt
2839 << "trackQAHolder.dTofdX=" << trackQAHolder.dTofdX
2840 << "trackQAHolder.dTofdZ=" << trackQAHolder.dTofdZ
2841 << "scaleTOF=" << scaleTOF
2842 << "\n";
2843 }
2844 }
2845 }
2846 }
2847
2848 return trackQAHolder;
2849}
2850
2851bool AODProducerWorkflowDPL::propagateTrackToPV(o2::track::TrackParametrizationWithError<float>& trackPar,
2853 int colID)
2854{
2855 o2::dataformats::DCA dcaInfo;
2856 dcaInfo.set(999.f, 999.f, 999.f, 999.f, 999.f);
2857 o2::dataformats::VertexBase v = mVtx.getMeanVertex(colID < 0 ? 0.f : data.getPrimaryVertex(colID).getZ());
2858 return o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trackPar, 2.f, mMatCorr, &dcaInfo);
2859}
2860
2861void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfoHolder, const o2::track::TrackPar& track)
2862{
2863 constexpr float XEMCAL = 440.f, XPHOS = 460.f, XEMCAL2 = XEMCAL * XEMCAL;
2864 constexpr float ETAEMCAL = 0.75; // eta of EMCAL/DCAL with margin
2865 constexpr float ZEMCALFastCheck = 460.; // Max Z (with margin to check with straightline extrapolarion)
2866 constexpr float ETADCALINNER = 0.22; // eta of the DCAL PHOS Hole (at XEMCAL)
2867 constexpr float ETAPHOS = 0.13653194; // nominal eta of the PHOS acceptance (at XPHOS): -log(tan((TMath::Pi()/2 - atan2(63, 460))/2))
2868 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
2869 constexpr float ETADCALPHOSSWITCH = (ETADCALINNER + ETAPHOS) / 2; // switch to DCAL to PHOS check if eta < this value
2870 constexpr short SNONE = 0, SEMCAL = 0x1, SPHOS = 0x2;
2871 constexpr short SECTORTYPE[18] = {
2872 SNONE, SNONE, SNONE, SNONE, // 0:3
2873 SEMCAL, SEMCAL, SEMCAL, SEMCAL, SEMCAL, SEMCAL, // 3:9 EMCAL only
2874 SNONE, SNONE, // 10:11
2875 SPHOS, // 12 PHOS only
2876 SPHOS | SEMCAL, SPHOS | SEMCAL, SPHOS | SEMCAL, // 13:15 PHOS & DCAL
2877 SEMCAL, // 16 DCAL only
2878 SNONE // 17
2879 };
2880
2881 o2::track::TrackPar outTr{track};
2882 auto prop = o2::base::Propagator::Instance();
2883 // 1st propagate to EMCAL nominal radius
2884 float xtrg = 0;
2885 // quick check with straight line propagtion
2886 if (!outTr.getXatLabR(XEMCAL, xtrg, prop->getNominalBz(), o2::track::DirType::DirOutward) ||
2887 (std::abs(outTr.getZAt(xtrg, 0)) > ZEMCALFastCheck) ||
2888 !prop->PropagateToXBxByBz(outTr, xtrg, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT)) {
2889 LOGP(debug, "preliminary step: does not reach R={} {}", XEMCAL, outTr.asString());
2890 return;
2891 }
2892 // we do not necessarilly reach wanted radius in a single propagation
2893 if ((outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY() < XEMCAL2) &&
2894 (!outTr.rotateParam(outTr.getPhi()) ||
2895 !outTr.getXatLabR(XEMCAL, xtrg, prop->getNominalBz(), o2::track::DirType::DirOutward) ||
2896 !prop->PropagateToXBxByBz(outTr, xtrg, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT))) {
2897 LOGP(debug, "does not reach R={} {}", XEMCAL, outTr.asString());
2898 return;
2899 }
2900 // rotate to proper sector
2901 int sector = o2::math_utils::angle2Sector(outTr.getPhiPos());
2902
2903 auto propExactSector = [&outTr, &sector, prop](float xprop) -> bool { // propagate exactly to xprop in the proper sector frame
2904 int ntri = 0;
2905 while (ntri < 2) {
2906 auto outTrTmp = outTr;
2907 float alpha = o2::math_utils::sector2Angle(sector);
2908 if ((std::abs(outTr.getZ()) > ZEMCALFastCheck) || !outTrTmp.rotateParam(alpha) ||
2909 !prop->PropagateToXBxByBz(outTrTmp, xprop, 0.95, 10, o2::base::Propagator::MatCorrType::USEMatCorrLUT)) {
2910 LOGP(debug, "failed on rotation to {} (sector {}) or propagation to X={} {}", alpha, sector, xprop, outTrTmp.asString());
2911 return false;
2912 }
2913 // make sure we are still in the target sector
2914 int sectorTmp = o2::math_utils::angle2Sector(outTrTmp.getPhiPos());
2915 if (sectorTmp == sector) {
2916 outTr = outTrTmp;
2917 break;
2918 }
2919 sector = sectorTmp;
2920 ntri++;
2921 }
2922 if (ntri == 2) {
2923 LOGP(debug, "failed to rotate to sector, {}", outTr.asString());
2924 return false;
2925 }
2926 return true;
2927 };
2928
2929 // we are at the EMCAL X, check if we are in the good sector
2930 if (!propExactSector(XEMCAL) || SECTORTYPE[sector] == SNONE) { // propagation failed or neither EMCAL not DCAL/PHOS
2931 return;
2932 }
2933
2934 // check if we are in a good eta range
2935 float r = std::sqrt(outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY()), tg = std::atan2(r, outTr.getZ());
2936 float eta = -std::log(std::tan(0.5f * tg)), etaAbs = std::abs(eta);
2937 if (etaAbs > ETAEMCAL) {
2938 LOGP(debug, "eta = {} is off at EMCAL radius", eta, outTr.asString());
2939 return;
2940 }
2941 // are we in the PHOS hole (with margin)?
2942 if ((SECTORTYPE[sector] & SPHOS) && etaAbs < ETADCALPHOSSWITCH) { // propagate to PHOS radius
2943 if (!propExactSector(XPHOS)) {
2944 return;
2945 }
2946 r = std::sqrt(outTr.getX() * outTr.getX() + outTr.getY() * outTr.getY());
2947 tg = std::atan2(r, outTr.getZ());
2948 eta = -std::log(std::tan(0.5f * tg));
2949 } else if (!(SECTORTYPE[sector] & SEMCAL)) { // are in the sector with PHOS only
2950 return;
2951 }
2952 extraInfoHolder.trackPhiEMCAL = outTr.getPhiPos();
2953 extraInfoHolder.trackEtaEMCAL = eta;
2954 LOGP(debug, "eta = {} phi = {} sector {} for {}", extraInfoHolder.trackEtaEMCAL, extraInfoHolder.trackPhiEMCAL, sector, outTr.asString());
2955 //
2956}
2957
2958std::set<uint64_t> AODProducerWorkflowDPL::filterEMCALIncomplete(const gsl::span<const o2::emcal::TriggerRecord> triggers)
2959{
2960 std::set<uint64_t> emcalIncompletes;
2961 for (const auto& trg : triggers) {
2962 if (trg.getTriggerBits() & o2::emcal::triggerbits::Inc) {
2963 // trigger record masked at incomplete at readout level
2964 emcalIncompletes.insert(trg.getBCData().toLong());
2965 }
2966 }
2967 return emcalIncompletes;
2968}
2969
2970void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc)
2971{
2973 static bool initOnceDone = false;
2974 if (!initOnceDone) { // this params need to be queried only once
2975 initOnceDone = true;
2976 // Note: DPLAlpideParam for ITS and MFT will be loaded by the RecoContainer
2977 mSqrtS = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS();
2978 // apply settings
2981 std::bitset<3564> bs = bcf.getBCPattern();
2982 for (auto i = 0U; i < bs.size(); i++) {
2983 if (bs.test(i)) {
2985 }
2986 }
2987
2989 mITSROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsITS.roFrameLengthTrig);
2992 mMFTROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsMFT.roFrameLengthTrig);
2994
2995 // RS FIXME: this is not yet fetched from the CCDB
2997 mTPCBinNS = elParam.ZbinWidth * 1.e3;
2998
2999 const auto& pvParams = o2::vertexing::PVertexerParams::Instance();
3000 mNSigmaTimeTrack = pvParams.nSigmaTimeTrack;
3001 mTimeMarginTrackTime = pvParams.timeMarginTrackTime * 1.e3;
3002 mFieldON = std::abs(o2::base::Propagator::Instance()->getNominalBz()) > 0.01;
3003
3004 pc.inputs().get<o2::ctp::CTPConfiguration*>("ctpconfig");
3005 }
3006 if (mPropTracks) {
3008 }
3009}
3010
3011//_______________________________________
3013{
3014 // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level
3015 if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) {
3016 if (matcher == ConcreteDataMatcher("GLO", "GRPMAGFIELD", 0)) {
3018 }
3019 return;
3020 }
3021 if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) {
3022 LOG(info) << "ITS Alpide param updated";
3024 par.printKeyValues();
3025 return;
3026 }
3027 if (matcher == ConcreteDataMatcher("MFT", "ALPIDEPARAM", 0)) {
3028 LOG(info) << "MFT Alpide param updated";
3030 par.printKeyValues();
3031 return;
3032 }
3033 if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) {
3034 LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString();
3035 mVtx = *(const o2::dataformats::MeanVertexObject*)obj;
3036 return;
3037 }
3038 if (matcher == ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) {
3039 // construct mask with EMCAL trigger classes for rejection of incomplete triggers
3040 auto ctpconfig = *(const o2::ctp::CTPConfiguration*)obj;
3041 mEMCALTrgClassMask = 0;
3042 for (const auto& trgclass : ctpconfig.getCTPClasses()) {
3043 if (trgclass.cluster->maskCluster[o2::detectors::DetID::EMC]) {
3044 mEMCALTrgClassMask |= trgclass.classMask;
3045 }
3046 }
3047 LOG(info) << "Loaded EMCAL trigger class mask: " << std::bitset<64>(mEMCALTrgClassMask);
3048 }
3049}
3050
3051void AODProducerWorkflowDPL::addRefGlobalBCsForTOF(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span<const GIndex>& GIndices,
3052 const o2::globaltracking::RecoContainer& data, std::map<uint64_t, int>& bcsMap)
3053{
3054 // Orphan tracks need to refer to some globalBC and for tracks with TOF this BC should be whithin an orbit
3055 // from the track abs time (to guarantee time precision). Therefore, we may need to insert some dummy globalBCs
3056 // to guarantee proper reference.
3057 // complete globalBCs by dummy entries necessary to provide BC references for TOF tracks with requested precision
3058 // to provide a reference for the time of the orphan tracks we should make sure that there are no gaps longer
3059 // than needed to store the time with sufficient precision
3060
3061 // estimate max distance in BCs between TOF time and eventual reference BC
3062 int nbitsFrac = 24 - (32 - o2::math_utils::popcount(mTrackTime)); // number of bits used to encode the fractional part of float truncated by the mask
3063 int nbitsLoss = std::max(0, int(std::log2(TOFTimePrecPS))); // allowed bit loss guaranteeing needed precision in PS
3064 assert(nbitsFrac > 1);
3065 std::uint64_t maxRangePS = std::uint64_t(0x1) << (nbitsFrac + nbitsLoss);
3066 int maxGapBC = maxRangePS / (o2::constants::lhc::LHCBunchSpacingNS * 1e3); // max gap in BCs allowing to store time with required precision
3067 LOG(info) << "Max gap of " << maxGapBC << " BCs to closest globalBC reference is needed for TOF tracks to provide precision of "
3068 << TOFTimePrecPS << " ps";
3069
3070 // check if there are tracks orphan tracks at all
3071 if (!trackRef.getEntries()) {
3072 return;
3073 }
3074 // the bscMap has at least TF start BC
3075 std::uint64_t maxBC = mStartIR.toLong();
3076 const auto& tofClus = data.getTOFClusters();
3077 for (int src = GIndex::NSources; src--;) {
3078 if (!GIndex::getSourceDetectorsMask(src)[o2::detectors::DetID::TOF]) { // check only tracks with TOF contribution
3079 continue;
3080 }
3081 int start = trackRef.getFirstEntryOfSource(src);
3082 int end = start + trackRef.getEntriesOfSource(src);
3083 for (int ti = start; ti < end; ti++) {
3084 auto& trackIndex = GIndices[ti];
3085 const auto& tofMatch = data.getTOFMatch(trackIndex);
3086 const auto& tofInt = tofMatch.getLTIntegralOut();
3087 float intLen = tofInt.getL();
3088 float tofExpMom = 0.;
3089 if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) {
3090 float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed));
3091 if (expBeta > o2::constants::math::Almost1) {
3093 }
3094 tofExpMom = o2::constants::physics::MassPionCharged * expBeta / std::sqrt(1.f - expBeta * expBeta);
3095 } else {
3096 continue;
3097 }
3098 double massZ = o2::track::PID::getMass2Z(data.getTrackParam(trackIndex).getPID());
3099 double energy = sqrt((massZ * massZ) + (tofExpMom * tofExpMom));
3100 double exp = intLen * energy / (cSpeed * tofExpMom);
3101 auto tofSignal = (tofMatch.getSignal() - exp) * 1e-3; // time in ns wrt TF start
3102 auto bc = relativeTime_to_GlobalBC(tofSignal);
3103
3104 auto it = bcsMap.lower_bound(bc);
3105 if (it == bcsMap.end() || it->first > bc + maxGapBC) {
3106 bcsMap.emplace_hint(it, bc, 1);
3107 LOG(debug) << "adding dummy BC " << bc;
3108 }
3109 if (bc > maxBC) {
3110 maxBC = bc;
3111 }
3112 }
3113 }
3114 // make sure there is a globalBC exceeding the max encountered bc
3115 if ((--bcsMap.end())->first <= maxBC) {
3116 bcsMap.emplace_hint(bcsMap.end(), maxBC + 1, 1);
3117 }
3118 // renumber BCs
3119 int bcID = 0;
3120 for (auto& item : bcsMap) {
3121 item.second = bcID;
3122 bcID++;
3123 }
3124}
3125
3126std::uint64_t AODProducerWorkflowDPL::fillBCSlice(int (&slice)[2], double tmin, double tmax, const std::map<uint64_t, int>& bcsMap) const
3127{
3128 // for ambiguous tracks (no or multiple vertices) we store the BC slice corresponding to track time window used for track-vertex matching,
3129 // see VertexTrackMatcher::extractTracks creator method, i.e. central time estimated +- uncertainty defined as:
3130 // 1) for tracks having a gaussian time error: PVertexerParams.nSigmaTimeTrack * trackSigma + PVertexerParams.timeMarginTrackTime
3131 // 2) for tracks having time uncertainty in a fixed time interval (TPC,ITS,MFT..): half of the interval + PVertexerParams.timeMarginTrackTime
3132 // The track time in the TrackExtraInfo is stored in ns wrt the collision BC for unambigous tracks and wrt bcSlice[0] for ambiguous ones,
3133 // with convention for errors: trackSigma in case (1) and half of the time interval for case (2) above.
3134
3135 // find indices of widest slice of global BCs in the map compatible with provided BC range. bcsMap is guaranteed to be non-empty.
3136 // We also assume that tmax >= tmin.
3137
3138 uint64_t bcMin = relativeTime_to_GlobalBC(tmin), bcMax = relativeTime_to_GlobalBC(tmax);
3139
3140 /*
3141 // brute force way of searching bcs via direct binary search in the map
3142 auto lower = bcsMap.lower_bound(bcMin), upper = bcsMap.upper_bound(bcMax);
3143
3144 if (lower == bcsMap.end()) {
3145 --lower;
3146 }
3147 if (upper != lower) {
3148 --upper;
3149 }
3150 slice[0] = std::distance(bcsMap.begin(), lower);
3151 slice[1] = std::distance(bcsMap.begin(), upper);
3152 */
3153
3154 // faster way to search in bunch crossing via the accelerated bunch crossing lookup structure
3155 auto p = mBCLookup.lower_bound(bcMin);
3156 // assuming that bcMax will be >= bcMin and close to bcMin; we can find
3157 // the upper bound quickly by lineary iterating from p.first to the point where
3158 // the time becomes larger than bcMax.
3159 // (if this is not the case we could determine it with a similar call to mBCLookup)
3160 auto& bcvector = mBCLookup.getBCTimeVector();
3161 auto upperindex = p.first;
3162 while (upperindex < bcvector.size() && bcvector[upperindex] <= bcMax) {
3163 upperindex++;
3164 }
3165 if (upperindex != p.first) {
3166 upperindex--;
3167 }
3168 slice[0] = p.first;
3169 slice[1] = upperindex;
3170
3171 auto bcOfTimeRef = p.second - this->mStartIR.toLong();
3172 LOG(debug) << "BC slice t:" << tmin << " " << slice[0]
3173 << " t: " << tmax << " " << slice[1]
3174 << " bcref: " << bcOfTimeRef;
3175 return bcOfTimeRef;
3176}
3177
3178std::vector<uint8_t> AODProducerWorkflowDPL::fillBCFlags(const o2::globaltracking::RecoContainer& data, std::map<uint64_t, int>& bcsMap) const
3179{
3180 std::vector<uint8_t> flags(bcsMap.size());
3181
3182 // flag BCs belonging to UPC mode ITS ROFs
3183 auto bcIt = bcsMap.begin();
3184 auto itsrofs = data.getITSTracksROFRecords();
3187 for (auto& rof : itsrofs) {
3188 if (!rof.getFlag(o2::itsmft::ROFRecord::VtxUPCMode)) {
3189 continue;
3190 }
3191 uint64_t globalBC0 = rof.getBCData().toLong() + bROF, globalBC1 = globalBC0 + lROF - 1;
3192 // BCs are sorted, iterate until the start of ROF
3193 while (bcIt != bcsMap.end()) {
3194 if (bcIt->first < globalBC0) {
3195 ++bcIt;
3196 continue;
3197 }
3198 if (bcIt->first > globalBC1) {
3199 break;
3200 }
3201 flags[bcIt->second] |= o2::aod::bc::ITSUPCMode;
3202 ++bcIt;
3203 }
3204 }
3205 return flags;
3206}
3207
3209{
3210 LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots",
3211 mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1);
3212
3213 mStreamer.reset();
3214}
3215
3216DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun, bool enableFITextra)
3217{
3218 auto dataRequest = std::make_shared<DataRequest>();
3219 dataRequest->inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", CTPConfigPerRun));
3220
3221 dataRequest->requestTracks(src, useMC);
3222 dataRequest->requestPrimaryVertices(useMC);
3223 if (src[GID::CTP]) {
3224 dataRequest->requestCTPDigits(useMC);
3225 }
3226 if (enableSV) {
3227 dataRequest->requestSecondaryVertices(useMC);
3228 }
3229 if (enableStrangenessTracking) {
3230 dataRequest->requestStrangeTracks(useMC);
3231 LOGF(info, "requestStrangeTracks Finish");
3232 }
3233 if (src[GID::ITS]) {
3234 dataRequest->requestClusters(GIndex::getSourcesMask("ITS"), false);
3235 }
3236 if (src[GID::TPC]) {
3237 dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks
3238 }
3239 if (src[GID::TOF]) {
3240 dataRequest->requestTOFClusters(useMC);
3241 }
3242 if (src[GID::PHS]) {
3243 dataRequest->requestPHOSCells(useMC);
3244 }
3245 if (src[GID::TRD]) {
3246 dataRequest->requestTRDTracklets(false);
3247 }
3248 if (src[GID::EMC]) {
3249 dataRequest->requestEMCALCells(useMC);
3250 }
3251 if (src[GID::CPV]) {
3252 dataRequest->requestCPVClusters(useMC);
3253 }
3254
3255 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(true, // orbitResetTime
3256 true, // GRPECS=true
3257 true, // GRPLHCIF
3258 true, // GRPMagField
3259 true, // askMatLUT
3261 dataRequest->inputs,
3262 true); // query only once all objects except mag.field
3263
3264 dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1));
3265
3266 using namespace o2::aod;
3267 using namespace o2::aodproducer;
3268
3269 std::vector<OutputSpec> outputs{
3303 OutputSpec{"TFN", "TFNumber"},
3304 OutputSpec{"TFF", "TFFilename"},
3305 OutputSpec{"AMD", "AODMetadataKeys"},
3306 OutputSpec{"AMD", "AODMetadataVals"}};
3307
3308 if (useMC) {
3309 outputs.insert(outputs.end(),
3310 {OutputForTable<McCollisions>::spec(),
3311 OutputForTable<HepMCXSections>::spec(),
3312 OutputForTable<HepMCPdfInfos>::spec(),
3313 OutputForTable<HepMCHeavyIons>::spec(),
3314 OutputForTable<McMFTTrackLabels>::spec(),
3315 OutputForTable<McFwdTrackLabels>::spec(),
3316 OutputForTable<StoredMcParticles_001>::spec(),
3317 OutputForTable<McTrackLabels>::spec(),
3318 OutputForTable<McCaloLabels_001>::spec(),
3319 // todo: use addTableToOuput helper?
3320 // currently the description is MCCOLLISLABEL, so
3321 // the name in AO2D would be O2mccollislabel
3322 // addTableToOutput<McCollisionLabels>(outputs);
3323 {OutputLabel{"McCollisionLabels"}, "AOD", "MCCOLLISIONLABEL", 0, Lifetime::Timeframe}});
3324 }
3325
3326 return DataProcessorSpec{
3327 "aod-producer-workflow",
3328 dataRequest->inputs,
3329 outputs,
3330 AlgorithmSpec{adaptFromTask<AODProducerWorkflowDPL>(src, dataRequest, ggRequest, enableSV, useMC, enableFITextra)},
3331 Options{
3332 ConfigParamSpec{"run-number", VariantType::Int64, -1L, {"The run-number. If left default we try to get it from DPL header."}},
3333 ConfigParamSpec{"aod-timeframe-id", VariantType::Int64, -1L, {"Set timeframe number"}},
3334 ConfigParamSpec{"fill-calo-cells", VariantType::Int, 1, {"Fill calo cells into cell table"}},
3335 ConfigParamSpec{"enable-truncation", VariantType::Int, 1, {"Truncation parameter: 1 -- on, != 1 -- off"}},
3336 ConfigParamSpec{"lpmp-prod-tag", VariantType::String, "", {"LPMProductionTag"}},
3337 ConfigParamSpec{"anchor-pass", VariantType::String, "", {"AnchorPassName"}},
3338 ConfigParamSpec{"anchor-prod", VariantType::String, "", {"AnchorProduction"}},
3339 ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}},
3340 ConfigParamSpec{"created-by", VariantType::String, "", {"Who created this AO2D"}},
3341 ConfigParamSpec{"nthreads", VariantType::Int, std::max(1, int(std::thread::hardware_concurrency() / 2)), {"Number of threads"}},
3342 ConfigParamSpec{"reco-mctracks-only", VariantType::Int, 0, {"Store only reconstructed MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}},
3343 ConfigParamSpec{"ctpreadout-create", VariantType::Int, 0, {"Create CTP digits from detector readout and CTP inputs. !=1 -- off, 1 -- on"}},
3344 ConfigParamSpec{"emc-select-leading", VariantType::Bool, false, {"Flag to select if only the leading contributing particle for an EMCal cell should be stored"}},
3345 ConfigParamSpec{"propagate-tracks", VariantType::Bool, false, {"Propagate tracks (not used for secondary vertices) to IP"}},
3346 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)"}},
3347 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)"}},
3348 ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}},
3349 ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}},
3350 ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}},
3351 ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}},
3352 ConfigParamSpec{"trackqc-tpc-dca", VariantType::Float, 3.f, {"Keep TPC standalone track with this DCAxy to the PV"}},
3353 ConfigParamSpec{"trackqc-tpc-cls", VariantType::Int, 80, {"Keep TPC standalone track with this #clusters"}},
3354 ConfigParamSpec{"trackqc-tpc-pt", VariantType::Float, 0.2f, {"Keep TPC standalone track with this pt"}},
3355 ConfigParamSpec{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}},
3356 ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}},
3357 ConfigParamSpec{"mc-signal-filt", VariantType::Bool, false, {"Enable usage of signal filtering (only for MC with embedding)"}}}};
3358}
3359
3360} // 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 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.
Definition TFIDInfo.h:20
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