Skip to content

Commit e0cc544

Browse files
committed
Introduce MC tables to upcCandProducerBarrel
1 parent abd5072 commit e0cc544

1 file changed

Lines changed: 193 additions & 13 deletions

File tree

PWGUD/TableProducer/upcCandProducerBarrel.cxx

Lines changed: 193 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <DataFormatsFIT/Triggers.h>
2727
#include <DataFormatsParameters/AggregatedRunInfo.h>
2828
#include <DataFormatsParameters/GRPLHCIFData.h>
29+
#include <Framework/ASoA.h>
2930
#include <Framework/AnalysisDataModel.h>
3031
#include <Framework/AnalysisHelpers.h>
3132
#include <Framework/AnalysisTask.h>
@@ -39,14 +40,15 @@
3940
#include <TH1.h>
4041
#include <TH2.h>
4142

42-
#include <Rtypes.h>
43-
4443
#include <algorithm>
4544
#include <bitset>
4645
#include <cmath>
4746
#include <cstddef>
4847
#include <cstdint>
4948
#include <memory>
49+
#include <unordered_map>
50+
#include <unordered_set>
51+
#include <utility>
5052
#include <vector>
5153

5254
using namespace o2;
@@ -59,19 +61,37 @@ constexpr int NBCsPerOrbit = o2::constants::lhc::LHCMaxBunches;
5961
constexpr int MaxStoredDistance = 15;
6062
constexpr float MaxFITTime = 30.f;
6163

64+
constexpr int NoSignalMCRef = -1;
65+
constexpr int NoRecoCollision = -1;
66+
constexpr int MultipleRecoTracks = -2;
67+
68+
constexpr uint16_t MCTrackFakeMask = 1u << 15;
69+
70+
enum MCParticleFlag : uint8_t {
71+
kNoFlags = 0,
72+
kPhysicalPrimary = 1u << 0, // generated physical primary
73+
kHasRecoTrack = 1u << 1, // has at least one non-fake reconstructed track
74+
kHighMultiplicityCollision = 1u << 2, // matched track belongs to a collision with extra contributors
75+
kHasPVContributor = 1u << 3, // has a track contributing to the PV
76+
kHasAmbiguousTrack = 1u << 4 // has an ambiguous track
77+
};
78+
79+
using SignalRef = std::pair<int32_t, int16_t>;
6280
using DistanceMap = std::vector<std::vector<uint32_t>>;
6381

6482
DistanceMap buildMinimumDistanceMap(std::vector<uint32_t> const& tfIDs,
6583
int64_t bcSOR, int64_t nBCsPerTF,
66-
std::vector<int64_t>& activeBCs, uint32_t maxDistance)
84+
std::vector<int64_t>& activeBCs, uint32_t maxDistance,
85+
bool skipFirstExactMatch = false)
6786
{
6887
const uint32_t overflowDistance = maxDistance + 1;
6988
DistanceMap distances(tfIDs.size(), std::vector<uint32_t>(nBCsPerTF, overflowDistance));
7089
if (activeBCs.empty()) {
7190
return distances;
7291
}
92+
// Keep duplicate BCs so collisions at the same BC have distance zero
7393
std::sort(activeBCs.begin(), activeBCs.end());
74-
activeBCs.erase(std::unique(activeBCs.begin(), activeBCs.end()), activeBCs.end());
94+
// The forward-only cursor makes the scan linear after sorting
7595
size_t nextIndex = 0;
7696
for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) {
7797
const int64_t tfStartBC = bcSOR + tfIDs[iTF] * nBCsPerTF;
@@ -80,9 +100,14 @@ DistanceMap buildMinimumDistanceMap(std::vector<uint32_t> const& tfIDs,
80100
while (nextIndex < activeBCs.size() && activeBCs[nextIndex] < currentBC) {
81101
++nextIndex;
82102
}
103+
size_t rightIndex = nextIndex;
104+
// MC maps contain the queried collision itself, so skip one exact self-match
105+
if (skipFirstExactMatch && rightIndex < activeBCs.size() && activeBCs[rightIndex] == currentBC) {
106+
++rightIndex;
107+
}
83108
int64_t distance = overflowDistance;
84-
if (nextIndex < activeBCs.size()) {
85-
distance = std::min(distance, activeBCs[nextIndex] - currentBC);
109+
if (rightIndex < activeBCs.size()) {
110+
distance = std::min(distance, activeBCs[rightIndex] - currentBC);
86111
}
87112
if (nextIndex > 0) {
88113
distance = std::min(distance, currentBC - activeBCs[nextIndex - 1]);
@@ -143,6 +168,13 @@ DECLARE_SOA_COLUMN(Sign, sign, std::vector<int8_t>);
143168
DECLARE_SOA_COLUMN(MinimumDistanceFT0, minimumDistanceFT0, int8_t);
144169
DECLARE_SOA_COLUMN(MinimumDistanceFV0, minimumDistanceFV0, int8_t);
145170
DECLARE_SOA_COLUMN(MinimumDistanceFDD, minimumDistanceFDD, int8_t);
171+
DECLARE_SOA_COLUMN(Pdg, pdg, std::vector<int32_t>);
172+
DECLARE_SOA_COLUMN(MinimumDistanceMCCol, minimumDistanceMCCol, int8_t);
173+
DECLARE_SOA_COLUMN(RecoFlags, recoFlags, std::vector<uint8_t>); // See MCParticleFlag for the bit definitions
174+
DECLARE_SOA_COLUMN(RecoCollisionIds, recoCollisionIds, std::vector<int32_t>); // -1: no collision, -2: multiple tracks
175+
DECLARE_SOA_COLUMN(ParticleIds, particleIds, std::vector<int16_t>);
176+
DECLARE_SOA_COLUMN(Mult, mult, int32_t); // number of physical-primary MC particles in the collision
177+
DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); // whether the collision belongs to the configured signal source
146178
} // namespace o2::aod::upc_cand_prod_bar
147179

148180
namespace o2::aod
@@ -173,10 +205,39 @@ DECLARE_SOA_TABLE(UPCBarrelCands, "AOD", "UPCBARRELCANDS",
173205
upc_cand_prod_bar::MinimumDistanceFT0,
174206
upc_cand_prod_bar::MinimumDistanceFV0,
175207
upc_cand_prod_bar::MinimumDistanceFDD);
208+
209+
DECLARE_SOA_TABLE(UPCBarrelMcColls, "AOD", "UPCBMCCOLL",
210+
upc_cand_prod_bar::TfId,
211+
upc_cand_prod_bar::GlobalBC,
212+
upc_cand_prod_bar::PosX,
213+
upc_cand_prod_bar::PosY,
214+
upc_cand_prod_bar::PosZ,
215+
upc_cand_prod_bar::Px,
216+
upc_cand_prod_bar::Py,
217+
upc_cand_prod_bar::Pz,
218+
upc_cand_prod_bar::Pdg,
219+
upc_cand_prod_bar::Mult,
220+
upc_cand_prod_bar::MinimumDistanceMCCol,
221+
upc_cand_prod_bar::RecoFlags,
222+
upc_cand_prod_bar::RecoCollisionIds,
223+
upc_cand_prod_bar::IsSignal);
224+
225+
namespace upc_cand_prod_bar
226+
{
227+
// Array index into UPCBarrelMcColls
228+
DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL_CUSTOM(McCollision, mcCollision, int32_t, UPCBarrelMcColls, "UPCBMCColls", "");
229+
} // namespace upc_cand_prod_bar
230+
231+
// Both IDs are -1 if the track has no valid stored signal reference
232+
DECLARE_SOA_TABLE(UPCBarrelMCLabels, "AOD", "UPCBMCLABEL",
233+
upc_cand_prod_bar::McCollisionIds,
234+
upc_cand_prod_bar::ParticleIds);
176235
} // namespace o2::aod
177236

178237
struct UpcCandProducerBarrel {
179238
Produces<aod::UPCBarrelCands> selectedCandidates;
239+
Produces<aod::UPCBarrelMcColls> selectedMCCollisions;
240+
Produces<aod::UPCBarrelMCLabels> selectedCandidateMCLabels;
180241
HistogramRegistry registry{"registry", {}};
181242
Service<o2::ccdb::BasicCCDBManager> ccdb{};
182243

@@ -197,13 +258,21 @@ struct UpcCandProducerBarrel {
197258
Configurable<int> vetoFT0{"vetoFT0", 1, "FT0 veto: 0=off, 1=on"};
198259
Configurable<int> vetoFV0{"vetoFV0", 0, "FV0 veto: 0=off, 1=on"};
199260
Configurable<int> vetoFDD{"vetoFDD", 0, "FDD veto: 0=off, 1=on"};
261+
Configurable<int> signalSourceId{"signalSourceId", 1, "MC source ID stored as embedded signal"};
200262

201263
using CollisionsWithSels = soa::Join<aod::Collisions, aod::EvSels>;
202264
using BCsWithSels = soa::Join<aod::BCsWithTimestamps, aod::BcSels>;
203265
using TracksWithPID = soa::Join<aod::Tracks, aod::TracksExtra,
204266
aod::pidTPCEl, aod::pidTPCPi, aod::pidTPCKa, aod::pidTPCPr,
205267
aod::pidTOFEl, aod::pidTOFPi, aod::pidTOFKa, aod::pidTOFPr>;
268+
using TracksWithPIDMC = soa::Join<aod::Tracks, aod::TracksExtra,
269+
aod::pidTPCEl, aod::pidTPCPi, aod::pidTPCKa, aod::pidTPCPr,
270+
aod::pidTOFEl, aod::pidTOFPi, aod::pidTOFKa, aod::pidTOFPr,
271+
aod::McTrackLabels>;
206272
Preslice<TracksWithPID> tracksPerCollision = aod::track::collisionId;
273+
Preslice<TracksWithPIDMC> mcTracksPerCollision = aod::track::collisionId;
274+
PresliceUnsorted<TracksWithPIDMC> tracksPerMCParticle = aod::mctracklabel::mcParticleId;
275+
Preslice<aod::McParticles> particlesPerMCCollision = aod::mcparticle::mcCollisionId;
207276

208277
RCTFlagsChecker rctChecker{kFDDBad, kFT0Bad, kFV0Bad, kITSBad, kITSLimAccMCRepr, kTPCBadTracking, kTPCLimAccMCRepr, kTPCBadPID, kTOFBad, kTOFLimAccMCRepr, kCcdbObjectLoaded};
209278

@@ -250,13 +319,19 @@ struct UpcCandProducerBarrel {
250319
cachedRunNumber = runNumber;
251320
}
252321

253-
void process(CollisionsWithSels const& collisions, TracksWithPID const& tracks, BCsWithSels const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0s, aod::FDDs const& fdds)
322+
template <bool isMC, typename CollisionsT, typename TracksT, typename PresliceT>
323+
void processImpl(CollisionsT const& collisions, TracksT const& tracks, PresliceT const& tracksPerColl,
324+
BCsWithSels const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0s,
325+
aod::FDDs const& fdds, aod::McCollisions const* mcCollisions = nullptr,
326+
aod::McParticles const* mcParticles = nullptr,
327+
aod::AmbiguousTracks const* ambiguousTracks = nullptr)
254328
{
255329
updateRunInfo(bcs.begin().runNumber());
256330
std::vector<uint32_t> tfIDs;
257331
std::vector<uint8_t> tfPassesRCT;
258332
std::vector<size_t> localTFIndex;
259333
localTFIndex.reserve(bcs.size());
334+
// Cache the local TF slot for each BC row for collision and MC lookups
260335
for (const auto& bc : bcs) {
261336
const auto tfID = static_cast<uint32_t>((static_cast<int64_t>(bc.globalBC()) - bcSOR) / nBCsPerTF);
262337
if (tfIDs.empty() || tfID != tfIDs.back()) {
@@ -267,12 +342,84 @@ struct UpcCandProducerBarrel {
267342
}
268343
registry.fill(HIST("hProcessedTFs"), 0.5, static_cast<double>(tfIDs.size()));
269344

345+
std::unordered_map<int64_t, SignalRef> signalParticleRefs;
346+
if constexpr (isMC) {
347+
std::unordered_set<int64_t> ambiguousTrackIds;
348+
ambiguousTrackIds.reserve(ambiguousTracks->size());
349+
for (const auto& ambiguousTrack : *ambiguousTracks) {
350+
ambiguousTrackIds.insert(ambiguousTrack.trackId());
351+
}
352+
353+
std::vector<int64_t> bcsWithMCCollision;
354+
bcsWithMCCollision.reserve(mcCollisions->size());
355+
// Use collisions from every source and retain same-BC pile-up
356+
for (const auto& mcCollision : *mcCollisions) {
357+
bcsWithMCCollision.push_back(static_cast<int64_t>(mcCollision.template bc_as<BCsWithSels>().globalBC()));
358+
}
359+
const auto minDistanceMC = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithMCCollision, MaxStoredDistance, true);
360+
361+
for (const auto& mcCollision : *mcCollisions) {
362+
const auto bc = mcCollision.template bc_as<BCsWithSels>();
363+
const auto globalBC = bc.globalBC();
364+
const auto iTF = localTFIndex[bc.globalIndex()];
365+
const auto bcInTF = (static_cast<int64_t>(globalBC) - bcSOR) % nBCsPerTF;
366+
const auto particles = mcParticles->sliceBy(particlesPerMCCollision, mcCollision.globalIndex());
367+
const bool isSignal = mcCollision.getSourceId() == signalSourceId.value;
368+
369+
std::vector<float> px, py, pz;
370+
std::vector<int32_t> pdg, recoCollisionIds;
371+
std::vector<uint8_t> recoFlags;
372+
int32_t mult = 0;
373+
// Output rows follow input MC collisions, making this the compact collision label
374+
const auto newMCCollisionId = static_cast<int32_t>(selectedMCCollisions.lastIndex() + 1);
375+
for (const auto& particle : particles) {
376+
mult += particle.isPhysicalPrimary();
377+
// Background rows keep only collision metadata and physical-primary multiplicity
378+
if (!isSignal) {
379+
continue;
380+
}
381+
const auto newMCParticleId = static_cast<int16_t>(pdg.size());
382+
px.push_back(particle.px());
383+
py.push_back(particle.py());
384+
pz.push_back(particle.pz());
385+
pdg.push_back(particle.pdgCode());
386+
387+
uint8_t flags = particle.isPhysicalPrimary() ? kPhysicalPrimary : kNoFlags;
388+
int32_t recoCollisionId = NoRecoCollision;
389+
int nRecoTracks = 0;
390+
// Inspect only reconstructed tracks matched to this signal particle
391+
for (const auto& track : tracks.sliceBy(tracksPerMCParticle, particle.globalIndex())) {
392+
if ((track.mcMask() & MCTrackFakeMask) != 0u) {
393+
continue;
394+
}
395+
++nRecoTracks;
396+
flags |= kHasRecoTrack;
397+
flags |= track.isPVContributor() ? kHasPVContributor : kNoFlags;
398+
flags |= ambiguousTrackIds.contains(track.globalIndex()) ? kHasAmbiguousTrack : kNoFlags;
399+
if (track.has_collision()) {
400+
recoCollisionId = track.collisionId();
401+
flags |= collisions.iteratorAt(recoCollisionId).numContrib() > nTracks ? kHighMultiplicityCollision : kNoFlags;
402+
}
403+
}
404+
// A single collision label is meaningful only for exactly one reconstructed track
405+
if (nRecoTracks > 1) {
406+
recoCollisionId = MultipleRecoTracks;
407+
}
408+
recoFlags.push_back(flags);
409+
recoCollisionIds.push_back(recoCollisionId);
410+
// Remap original particle IDs to output index for candidate labels
411+
signalParticleRefs.emplace(particle.globalIndex(), SignalRef{newMCCollisionId, newMCParticleId});
412+
}
413+
selectedMCCollisions(tfIDs[iTF], globalBC, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(),
414+
px, py, pz, pdg, mult, minDistanceMC[iTF][bcInTF],
415+
recoFlags, recoCollisionIds, isSignal);
416+
}
417+
}
418+
270419
auto hTVX = registry.get<TH1>(HIST("hTVX"));
271420
auto hTVXRCT = registry.get<TH1>(HIST("hTVXRCT"));
272421

273-
std::vector<int64_t> bcsWithFT0;
274-
std::vector<int64_t> bcsWithFV0;
275-
std::vector<int64_t> bcsWithFDD;
422+
std::vector<int64_t> bcsWithFT0, bcsWithFV0, bcsWithFDD;
276423
bcsWithFT0.reserve(ft0s.size());
277424
bcsWithFV0.reserve(fv0s.size());
278425
bcsWithFDD.reserve(fdds.size());
@@ -284,7 +431,7 @@ struct UpcCandProducerBarrel {
284431
if (ft0.timeA() < MaxFITTime || ft0.timeC() < MaxFITTime) {
285432
bcsWithFT0.push_back(gbc);
286433
}
287-
if (!collidingBCs[bcInOrbit] || !TESTBIT(ft0.triggerMask(), o2::fit::Triggers::bitVertex)) {
434+
if (!collidingBCs[bcInOrbit] || !(ft0.triggerMask() & (1U << o2::fit::Triggers::bitVertex))) {
288435
continue;
289436
}
290437
hTVX->Fill(bcInOrbit);
@@ -337,7 +484,7 @@ struct UpcCandProducerBarrel {
337484
if (collision.numContrib() != nTracks || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !rctChecker(collision)) {
338485
continue;
339486
}
340-
auto bc = collision.bc_as<BCsWithSels>();
487+
auto bc = collision.template bc_as<BCsWithSels>();
341488
auto gbc = bc.globalBC();
342489
const auto iTF = localTFIndex[bc.globalIndex()];
343490
const int64_t bcInTF = (static_cast<int64_t>(gbc) - bcSOR) % nBCsPerTF;
@@ -354,7 +501,9 @@ struct UpcCandProducerBarrel {
354501
std::vector<uint8_t> itsClusterMap;
355502
std::vector<uint8_t> nClusters;
356503
std::vector<int8_t> sign;
357-
for (const auto& track : tracks.sliceBy(tracksPerCollision, collision.globalIndex())) {
504+
std::vector<int32_t> newMCCollisionIds;
505+
std::vector<int16_t> newParticleIds;
506+
for (const auto& track : tracks.sliceBy(tracksPerColl, collision.globalIndex())) {
358507
if (!track.isPVContributor() || !track.hasITS() || !track.hasTPC() || std::abs(track.eta()) > maxAbsEta.value || track.pt() < minPt.value) {
359508
continue;
360509
}
@@ -373,6 +522,17 @@ struct UpcCandProducerBarrel {
373522
itsClusterMap.push_back(track.itsClusterMap());
374523
nClusters.push_back(track.tpcNClsFound());
375524
sign.push_back(track.sign());
525+
if constexpr (isMC) {
526+
// Fake, background, and unmatched particles keep the -1 label
527+
SignalRef label{NoSignalMCRef, NoSignalMCRef};
528+
if (track.has_mcParticle() && (track.mcMask() & MCTrackFakeMask) == 0u) {
529+
if (const auto ref = signalParticleRefs.find(track.mcParticleId()); ref != signalParticleRefs.end()) {
530+
label = ref->second;
531+
}
532+
}
533+
newMCCollisionIds.push_back(label.first);
534+
newParticleIds.push_back(label.second);
535+
}
376536
}
377537
if (px.size() != static_cast<size_t>(nTracks)) {
378538
continue;
@@ -383,8 +543,28 @@ struct UpcCandProducerBarrel {
383543
std::min<uint32_t>(distFT0, MaxStoredDistance + 1),
384544
std::min<uint32_t>(distFV0, MaxStoredDistance + 1),
385545
std::min<uint32_t>(distFDD, MaxStoredDistance + 1));
546+
if constexpr (isMC) {
547+
selectedCandidateMCLabels(newMCCollisionIds, newParticleIds);
548+
}
386549
}
387550
}
551+
552+
void processData(CollisionsWithSels const& collisions, TracksWithPID const& tracks, BCsWithSels const& bcs,
553+
aod::FT0s const& ft0s, aod::FV0As const& fv0s, aod::FDDs const& fdds)
554+
{
555+
processImpl<false>(collisions, tracks, tracksPerCollision, bcs, ft0s, fv0s, fdds);
556+
}
557+
PROCESS_SWITCH(UpcCandProducerBarrel, processData, "Process reconstructed data", true);
558+
559+
void processMC(CollisionsWithSels const& collisions, TracksWithPIDMC const& tracks, BCsWithSels const& bcs,
560+
aod::FT0s const& ft0s, aod::FV0As const& fv0s, aod::FDDs const& fdds,
561+
aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles,
562+
aod::AmbiguousTracks const& ambiguousTracks)
563+
{
564+
processImpl<true>(collisions, tracks, mcTracksPerCollision, bcs, ft0s, fv0s, fdds,
565+
&mcCollisions, &mcParticles, &ambiguousTracks);
566+
}
567+
PROCESS_SWITCH(UpcCandProducerBarrel, processMC, "Process reconstructed MC and write compact MC truth", false);
388568
};
389569

390570
WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{adaptAnalysisTask<UpcCandProducerBarrel>(context)}; }

0 commit comments

Comments
 (0)