Skip to content

Commit f6befa1

Browse files
authored
[PWGCF] Fix histogram filling in femto framework (#18691)
1 parent b2f2c7d commit f6befa1

27 files changed

Lines changed: 855 additions & 587 deletions

‎PWGCF/Femto/Core/collisionHistManager.h‎

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,17 @@ enum ColHist {
6565
kColHistLast
6666
};
6767

68-
constexpr std::string_view AnalysisDir = "Collisions/Analysis/";
69-
constexpr std::string_view QaDir = "Collisions/QA/";
70-
constexpr std::string_view McDir = "Collisions/MC/";
68+
constexpr std::string_view AnalysisDir = "Analysis/";
69+
constexpr std::string_view QaDir = "QA/";
70+
constexpr std::string_view McDir = "MC/";
71+
72+
// prefixes: dedicated to the collision histograms, independent of whatever pair/triplet-type
73+
// prefix the enclosing builder uses for its other managers -- keeps every collision histogram
74+
// under one shared "Collision/" directory regardless of which pair/triplet type produced it.
75+
// Standalone QA tasks have no SE/ME split, so they use PrefixQa directly.
76+
constexpr char PrefixQa[] = "Collision/";
77+
constexpr char PrefixSe[] = "Collision/SE/";
78+
constexpr char PrefixMe[] = "Collision/ME/";
7179

7280
constexpr std::array<histmanager::HistInfo<ColHist>, kColHistLast> HistTable = {
7381
{
@@ -192,6 +200,7 @@ struct ConfCollisionQaBinning : o2::framework::ConfigurableGroup {
192200
o2::framework::ConfigurableAxis occupancy{"occupancy", {500, 0.f, 5000.f}, "Occupancy Binning"};
193201
};
194202

203+
template <auto& prefix>
195204
class CollisionHistManager
196205
{
197206
public:
@@ -270,7 +279,7 @@ class CollisionHistManager
270279
private:
271280
void initAnalysis(std::map<ColHist, std::vector<o2::framework::AxisSpec>> const& Specs)
272281
{
273-
std::string analysisDir = std::string(AnalysisDir);
282+
std::string analysisDir = std::string(prefix) + std::string(AnalysisDir);
274283
mHistogramRegistry->add(analysisDir + getHistNameV2(kPosZ, HistTable), getHistDesc(kPosZ, HistTable), getHistType(kPosZ, HistTable), {Specs.at(kPosZ)});
275284
mHistogramRegistry->add(analysisDir + getHistNameV2(kMult, HistTable), getHistDesc(kMult, HistTable), getHistType(kMult, HistTable), {Specs.at(kMult)});
276285
mHistogramRegistry->add(analysisDir + getHistNameV2(kCent, HistTable), getHistDesc(kCent, HistTable), getHistType(kCent, HistTable), {Specs.at(kCent)});
@@ -284,7 +293,7 @@ class CollisionHistManager
284293

285294
void initQa(std::map<ColHist, std::vector<o2::framework::AxisSpec>> const& Specs)
286295
{
287-
std::string qaDir = std::string(QaDir);
296+
std::string qaDir = std::string(prefix) + std::string(QaDir);
288297
mHistogramRegistry->add(qaDir + getHistNameV2(kPosX, HistTable), getHistDesc(kPosX, HistTable), getHistType(kPosX, HistTable), {Specs.at(kPosX)});
289298
mHistogramRegistry->add(qaDir + getHistNameV2(kPosY, HistTable), getHistDesc(kPosY, HistTable), getHistType(kPosY, HistTable), {Specs.at(kPosY)});
290299
mHistogramRegistry->add(qaDir + getHistNameV2(kPos, HistTable), getHistDesc(kPos, HistTable), getHistType(kPos, HistTable), {Specs.at(kPos)});
@@ -303,7 +312,7 @@ class CollisionHistManager
303312
// reco-vs-truth correlation histograms (kReco and kMc both set)
304313
void initMc(std::map<ColHist, std::vector<o2::framework::AxisSpec>> const& Specs)
305314
{
306-
std::string mcDir = std::string(McDir);
315+
std::string mcDir = std::string(prefix) + std::string(McDir);
307316
mHistogramRegistry->add(mcDir + getHistNameV2(kTruePosZVsPosZ, HistTable), getHistDesc(kTruePosZVsPosZ, HistTable), getHistType(kTruePosZVsPosZ, HistTable), {Specs.at(kTruePosZVsPosZ)});
308317
mHistogramRegistry->add(mcDir + getHistNameV2(kTrueCentVsCent, HistTable), getHistDesc(kTrueCentVsCent, HistTable), getHistType(kTrueCentVsCent, HistTable), {Specs.at(kTrueCentVsCent)});
309318
mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMultVsMult, HistTable), getHistDesc(kTrueMultVsMult, HistTable), getHistType(kTrueMultVsMult, HistTable), {Specs.at(kTrueMultVsMult)});
@@ -312,7 +321,7 @@ class CollisionHistManager
312321
// pure mc-truth collision: 1D only, no reco collision exists (kMc without kReco)
313322
void initMcTruth(std::map<ColHist, std::vector<o2::framework::AxisSpec>> const& Specs)
314323
{
315-
std::string mcDir = std::string(McDir);
324+
std::string mcDir = std::string(prefix) + std::string(McDir);
316325
mHistogramRegistry->add(mcDir + getHistNameV2(kTruePosZ, HistTable), getHistDesc(kTruePosZ, HistTable), getHistType(kTruePosZ, HistTable), {Specs.at(kTruePosZ)});
317326
mHistogramRegistry->add(mcDir + getHistNameV2(kTrueCent, HistTable), getHistDesc(kTrueCent, HistTable), getHistType(kTrueCent, HistTable), {Specs.at(kTrueCent)});
318327
mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMult, HistTable), getHistDesc(kTrueMult, HistTable), getHistType(kTrueMult, HistTable), {Specs.at(kTrueMult)});
@@ -321,34 +330,34 @@ class CollisionHistManager
321330
template <typename T>
322331
void fillAnalysis(T const& col)
323332
{
324-
mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kPosZ, HistTable)), col.posZ());
325-
mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kMult, HistTable)), col.mult());
326-
mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kCent, HistTable)), col.cent());
327-
mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kMagField, HistTable)), col.magField());
333+
mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kPosZ, HistTable)), col.posZ());
334+
mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kMult, HistTable)), col.mult());
335+
mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kCent, HistTable)), col.cent());
336+
mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kMagField, HistTable)), col.magField());
328337

329338
if (mPlotEventShape) {
330339
if constexpr (utils::HasEventShape<T>) {
331-
mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kQvector, HistTable)), col.qvec());
332-
mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kEventPlaneAngle, HistTable)), col.eventPlaneAngle());
340+
mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQvector, HistTable)), col.qvec());
341+
mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kEventPlaneAngle, HistTable)), col.eventPlaneAngle());
333342
}
334343
}
335344
}
336345

337346
template <typename T>
338347
void fillQa(T const& col)
339348
{
340-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kPosX, HistTable)), col.posX());
341-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kPosY, HistTable)), col.posY());
342-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kPos, HistTable)), std::hypot(col.posX(), col.posY(), col.posZ()));
343-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kSphericity, HistTable)), col.sphericity());
344-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kOccupancy, HistTable)), col.trackOccupancyInTimeRange());
349+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPosX, HistTable)), col.posX());
350+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPosY, HistTable)), col.posY());
351+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPos, HistTable)), std::hypot(col.posX(), col.posY(), col.posZ()));
352+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kSphericity, HistTable)), col.sphericity());
353+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kOccupancy, HistTable)), col.trackOccupancyInTimeRange());
345354
if (mPlot2d) {
346-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kPoszVsMult, HistTable)), col.posZ(), col.mult());
347-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kPoszVsCent, HistTable)), col.posZ(), col.cent());
348-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kCentVsMult, HistTable)), col.cent(), col.mult());
349-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kMultVsSphericity, HistTable)), col.mult(), col.sphericity());
350-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kCentVsSphericity, HistTable)), col.cent(), col.sphericity());
351-
mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kFT0AvsFT0C, HistTable)), col.centFT0A(), col.centFT0C());
355+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPoszVsMult, HistTable)), col.posZ(), col.mult());
356+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPoszVsCent, HistTable)), col.posZ(), col.cent());
357+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kCentVsMult, HistTable)), col.cent(), col.mult());
358+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kMultVsSphericity, HistTable)), col.mult(), col.sphericity());
359+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kCentVsSphericity, HistTable)), col.cent(), col.sphericity());
360+
mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kFT0AvsFT0C, HistTable)), col.centFT0A(), col.centFT0C());
352361
}
353362
}
354363

@@ -360,18 +369,18 @@ class CollisionHistManager
360369
return;
361370
}
362371
auto mcCol = col.template fMcCol_as<T2>();
363-
mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTruePosZVsPosZ, HistTable)), mcCol.posZ(), col.posZ());
364-
mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueCentVsCent, HistTable)), mcCol.cent(), col.cent());
365-
mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueMultVsMult, HistTable)), mcCol.mult(), col.mult());
372+
mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePosZVsPosZ, HistTable)), mcCol.posZ(), col.posZ());
373+
mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueCentVsCent, HistTable)), mcCol.cent(), col.cent());
374+
mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMultVsMult, HistTable)), mcCol.mult(), col.mult());
366375
}
367376

368377
// pure mc-truth collision: 'col' here IS the truth collision, no reco object exists
369378
template <typename T>
370379
void fillMc(T const& col)
371380
{
372-
mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTruePosZ, HistTable)), col.posZ());
373-
mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueCent, HistTable)), col.cent());
374-
mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueMult, HistTable)), col.mult());
381+
mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePosZ, HistTable)), col.posZ());
382+
mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueCent, HistTable)), col.cent());
383+
mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMult, HistTable)), col.mult());
375384
}
376385

377386
o2::framework::HistogramRegistry* mHistogramRegistry = nullptr;

‎PWGCF/Femto/Core/dndetaBuilder.h‎

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,13 @@ class DndetaBuilder
173173
// data
174174
// ---------------------------------------------------------------------------
175175

176+
/// \param triggerKinematic k*/Q3 of the triggering pair/triplet (minimum over the event), written
177+
/// by the trigger callable as a side effect on itself returning true; -1 (or whatever the
178+
/// caller initializes it to) for minimum bias. Read only after trigger() has run, i.e.
179+
/// only meaningful once this function returns true.
176180
/// \return true if the collision was selected (including the trigger)
177181
template <typename T1, typename T2, typename T3, typename T4, typename T5>
178-
bool processData(T1 const& col, T2& partitionGlobal, T3& partitionItsOnly, T4& cache, T5 const& trigger)
182+
bool processData(T1 const& col, T2& partitionGlobal, T3& partitionItsOnly, T4& cache, T5 const& trigger, float triggerKinematic = -1.f)
179183
{
180184
if (!selectCollision(col, trigger)) {
181185
return false;
@@ -194,15 +198,15 @@ class DndetaBuilder
194198
}
195199
nch++;
196200
mTrackHistManagerGlobal.template fill<modes::Mode::kReco_Qa>(track, tracksGlobal);
197-
mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), true);
201+
mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), true, triggerKinematic);
198202
}
199203
for (auto const& track : tracksItsOnly) {
200204
if (!isTrackAccepted(track, false)) {
201205
continue;
202206
}
203207
nch++;
204208
mTrackHistManagerItsOnly.template fill<modes::Mode::kReco_Qa>(track, tracksItsOnly);
205-
mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), false);
209+
mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), false, triggerKinematic);
206210
}
207211
if (mDoCorrelation) {
208212
mHistManager.fillCorrelation(static_cast<float>(nch), col.mult(), col.multFT0A(), col.multFT0C());
@@ -234,7 +238,11 @@ class DndetaBuilder
234238
/// - gen: efficiency histograms for all generated collisions, loss histograms for the generated event class
235239
/// (optional INEL>0, optional generator-level trigger); the loss block's reco requirement is Sel8
236240
/// only (no vtxZ, see isSelectedByMaskOnly), the vtxZ cut only gates the efficiency block above
237-
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11>
241+
/// \param triggerKinematicScratch scratch variable the recoTrigger callable writes the
242+
/// triggering k*/Q3 into as a side effect when it returns true (minimum over the event);
243+
/// read back immediately after each recoTrigger() call, so its value right before the
244+
/// call is irrelevant and it is freely reused across collisions
245+
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12>
238246
void processMc(T1 const& cols,
239247
T2 const& mcCols,
240248
T3& partitionGlobal,
@@ -245,7 +253,8 @@ class DndetaBuilder
245253
T8 const& perMcColParticles,
246254
T9& pdg,
247255
T10 const& recoTrigger,
248-
T11 const& genTrigger)
256+
T11 const& genTrigger,
257+
T12& triggerKinematicScratch)
249258
{
250259
const auto nMcCols = static_cast<std::size_t>(mcCols.size());
251260

@@ -270,6 +279,7 @@ class DndetaBuilder
270279
// not implemented (every Run3 event with a vertex is already within the stored range), so the loss-block
271280
// reco requirement must not carry the vtxZ cut like the Ctrk-side "selvtx" one does
272281
std::vector<int64_t> selectedReco(nMcCols, -1);
282+
std::vector<float> selectedRecoTriggerKinematic(nMcCols, -1.f);
273283
std::vector<bool> hasSel8Reco(nMcCols, false);
274284
for (auto const& col : cols) {
275285
if (!col.has_fMcCol() || bestReco[static_cast<std::size_t>(col.fMcColId())] != col.globalIndex()) {
@@ -284,10 +294,11 @@ class DndetaBuilder
284294
continue;
285295
}
286296
selectedReco[static_cast<std::size_t>(col.fMcColId())] = col.globalIndex();
297+
selectedRecoTriggerKinematic[static_cast<std::size_t>(col.fMcColId())] = triggerKinematicScratch;
287298
if (mDoMcEfficiency) {
288299
auto tracksGlobal = partitionGlobal->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache);
289300
auto tracksItsOnly = partitionItsOnly->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache);
290-
fillRecoMc(col, tracksGlobal, tracksItsOnly, mcParticles, mcMothers);
301+
fillRecoMc(col, tracksGlobal, tracksItsOnly, mcParticles, mcMothers, triggerKinematicScratch);
291302
}
292303
}
293304

@@ -300,13 +311,14 @@ class DndetaBuilder
300311
// efficiency: centrality of the selected reconstructed collision (underflow if there is none)
301312
if (mDoMcEfficiency) {
302313
const float recoCent = hasSelectedReco ? cols.rawIteratorAt(selectedReco[mcColIndex]).cent() : -1.f;
314+
const float triggerKinematic = hasSelectedReco ? selectedRecoTriggerKinematic[mcColIndex] : -1.f;
303315
mHistManager.fillGenCollision(mcCol.posZ(), recoCent, hasSelectedReco);
304316
for (auto const& particle : particles) {
305317
if (!isGenParticleSelected(particle, pdg)) {
306318
continue;
307319
}
308320
mHistManager.fillGenParticle(mcCol.posZ(), recoCent, particle.eta(), particle.phi(), genType(particle.pdgCode()), hasSelectedReco,
309-
ptVariationWeight(particle.pt(), mPtVariationUp), ptVariationWeight(particle.pt(), mPtVariationDown));
321+
ptVariationWeight(particle.pt(), mPtVariationUp), ptVariationWeight(particle.pt(), mPtVariationDown), triggerKinematic);
310322
}
311323
}
312324

@@ -492,20 +504,22 @@ class DndetaBuilder
492504
}
493505

494506
template <typename T1, typename T2, typename T3, typename T4>
495-
void fillRecoMcTrack(T1 const& col, T2 const& track, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, std::vector<int64_t>& usedLabels)
507+
void fillRecoMcTrack(T1 const& col, T2 const& track, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, std::vector<int64_t>& usedLabels, float triggerKinematic)
496508
{
497509
const float posZ = col.posZ();
498510
const float cent = col.cent();
499511
if (!track.has_fMcParticle()) {
500-
mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoBkg);
512+
mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoBkg, triggerKinematic);
501513
return;
502514
}
503515
auto mcParticle = track.template fMcParticle_as<T3>();
504-
// tracks matched to a particle from another mc collision are skipped (as in the legacy task)
516+
// a track whose matched mc particle belongs to a different mc collision is a mis-association
517+
// (e.g. an ambiguous/pileup-adjacent match); counting it here would attribute it to the wrong
518+
// collision's efficiency, so it is skipped
505519
if (mcParticle.fMcColId() != col.fMcColId()) {
506520
return;
507521
}
508-
mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoAll);
522+
mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoAll, triggerKinematic);
509523

510524
dndetahistmanager::RecoType type = dndetahistmanager::kRecoSecondary;
511525
if (isPhysicalPrimary(mcParticle)) {
@@ -521,25 +535,25 @@ class DndetaBuilder
521535
type = dndetahistmanager::kRecoFake;
522536
}
523537
usedLabels.push_back(track.fMcParticleId());
524-
mHistManager.fillRecoTrack(posZ, cent, mcParticle.eta(), mcParticle.phi(), type);
538+
mHistManager.fillRecoTrack(posZ, cent, mcParticle.eta(), mcParticle.phi(), type, triggerKinematic);
525539
}
526540

527541
template <typename T1, typename T2, typename T3, typename T4, typename T5>
528-
void fillRecoMc(T1 const& col, T2 const& tracksGlobal, T3 const& tracksItsOnly, T4 const& mcParticles, T5 const& mcMothers)
542+
void fillRecoMc(T1 const& col, T2 const& tracksGlobal, T3 const& tracksItsOnly, T4 const& mcParticles, T5 const& mcMothers, float triggerKinematic)
529543
{
530544
mHistManager.fillRecoCollision(col.posZ(), col.cent());
531545
// labels are tracked over both partitions, so a fake is also found if the other track comes from the other partition
532546
std::vector<int64_t> usedLabels;
533547
for (auto const& track : tracksGlobal) {
534548
if (isTrackAccepted(track, true)) {
535549
mTrackHistManagerGlobal.template fill<modes::Mode::kReco_Qa>(track, tracksGlobal);
536-
fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels);
550+
fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels, triggerKinematic);
537551
}
538552
}
539553
for (auto const& track : tracksItsOnly) {
540554
if (isTrackAccepted(track, false)) {
541555
mTrackHistManagerItsOnly.template fill<modes::Mode::kReco_Qa>(track, tracksItsOnly);
542-
fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels);
556+
fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels, triggerKinematic);
543557
}
544558
}
545559
}

0 commit comments

Comments
 (0)