Skip to content

Commit 824dbc8

Browse files
blacwoviealibuild
andauthored
[PWGCF] Add DCA fraction and purity QA to HadNucleiFemto (#18182)
Co-authored-by: ALICE Action Bot <alibuild@cern.ch>
1 parent 869d3a4 commit 824dbc8

1 file changed

Lines changed: 151 additions & 2 deletions

File tree

‎PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx‎

Lines changed: 151 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//
1212

1313
/// \file HadNucleiFemtoDcaPurity.cxx
14-
/// \brief Nuclei-hadron femtoscopy task with DCA-fraction and purity inputs
14+
/// \brief Nuclei-hadron femtoscopy task
1515
/// \author CMY
1616
/// \date 2025-04-10
1717

@@ -768,6 +768,28 @@ struct HadNucleiFemto {
768768
{"fraction/hDcaMotherPdgHad", "Hadron DCA by exact direct-mother PDG; signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast<float>(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast<float>(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast<float>(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast<float>(MotherPdgChunkBase) - 0.5f}}}},
769769
{"fraction/hDcaMotherPdgNu", "Nucleus DCA by exact direct-mother PDG; signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast<float>(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast<float>(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast<float>(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast<float>(MotherPdgChunkBase) - 0.5f}}}},
770770

771+
// Generator denominators and source-resolved response matrices for the
772+
// hypertriton-to-primary He3 and triton constraints. Generated momenta
773+
// are physical particle momenta and must never receive the reconstructed
774+
// He3-track x2 rigidity conversion. The two-body histograms are
775+
// conditional on their generated decay channel, so the physical
776+
// branching ratios remain independent offline inputs.
777+
{"fractionMCGen/hPrimaryHe3", "Physical-primary generated He3 in selected MC collisions;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
778+
{"fractionMCGen/hHypertriton", "Physical-primary generated hypertritons in selected MC collisions;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
779+
{"fractionMCGen/hHypertritonToHe3Pi", "Physical-primary generated hypertritons in the He3-pion channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
780+
{"fractionMCGen/hHypertritonDaughterHe3", "Generated direct He3 daughters in the hypertriton-He3-pion channel;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
781+
{"fractionMCGen/hHypertritonPtVsDaughterHe3Pt", "Generated hypertriton-to-He3 decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
782+
{"fractionMCGen/hPrimaryTriton", "Physical-primary generated tritons in selected MC collisions;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
783+
{"fractionMCGen/hHypertritonToTritonPi0", "Physical-primary generated hypertritons in the triton-pi0 channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
784+
{"fractionMCGen/hHypertritonDaughterTriton", "Generated direct triton daughters in the hypertriton-triton-pi0 channel;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
785+
{"fractionMCGen/hHypertritonPtVsDaughterTritonPt", "Generated hypertriton-to-triton decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
786+
{"fractionMCReco/hPrimaryHe3GenPtVsRecoPt", "DCA-selected physical-primary He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
787+
{"fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt", "DCA-selected hypertriton-daughter He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
788+
{"fractionMCReco/hHypertritonParentPtVsDaughterRecoPt", "DCA-selected hypertriton parent-to-He3 response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
789+
{"fractionMCReco/hPrimaryTritonGenPtVsRecoPt", "DCA-selected physical-primary triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
790+
{"fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt", "DCA-selected hypertriton-daughter triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
791+
{"fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt", "DCA-selected hypertriton parent-to-triton response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}},
792+
771793
// Hierarchical MC truth purity counters: all = correct species + mis-ID
772794
// + no label; correct species = correct collision + wrong collision;
773795
// correct collision = primary + weak + material.
@@ -3687,6 +3709,54 @@ struct HadNucleiFemto {
36873709
const float productionRadius = std::hypot(particle.vx(), particle.vy());
36883710
const float signedPt = signedPhysicalPt(track, isNucleus);
36893711

3712+
// These response matrices use the relaxed DCA-fit selection under which
3713+
// this function is called. Keep only correct-collision He3 or triton so
3714+
// the reconstructed numerators match the selected-MC-collision generator
3715+
// denominators filled below. Hypertriton responses are conditional on the
3716+
// explicit He3-pion or triton-pi0 channel; physical branching ratios are
3717+
// deliberately not folded into these histograms.
3718+
const int absoluteParticlePdg = std::abs(particle.pdgCode());
3719+
if (isNucleus && matchesRecoCollision && (absoluteParticlePdg == He3PDG || absoluteParticlePdg == TritonPDG)) {
3720+
const float signedGeneratedPt = particle.pdgCode() > 0 ? particle.pt() : -particle.pt();
3721+
const float generatedRapidity = particle.y();
3722+
if (origin == Primary) {
3723+
if (absoluteParticlePdg == He3PDG) {
3724+
mQaRegistry.fill(HIST("fractionMCReco/hPrimaryHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality);
3725+
} else {
3726+
mQaRegistry.fill(HIST("fractionMCReco/hPrimaryTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality);
3727+
}
3728+
} else if (origin == WeakDecay && particle.has_mothers()) {
3729+
for (const auto& mother : particle.template mothers_as<aod::McParticles>()) {
3730+
if (std::abs(mother.pdgCode()) != HyperTritonPDG || !mother.isPhysicalPrimary() ||
3731+
((mother.pdgCode() > 0) != (particle.pdgCode() > 0))) {
3732+
continue;
3733+
}
3734+
const int expectedPionPdg = absoluteParticlePdg == He3PDG
3735+
? (mother.pdgCode() > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus)
3736+
: PDG_t::kPi0;
3737+
bool hasExpectedPion = false;
3738+
for (const auto& daughter : mother.template daughters_as<aod::McParticles>()) {
3739+
if (daughter.pdgCode() == expectedPionPdg) {
3740+
hasExpectedPion = true;
3741+
break;
3742+
}
3743+
}
3744+
if (!hasExpectedPion) {
3745+
continue;
3746+
}
3747+
const float signedParentPt = mother.pdgCode() > 0 ? mother.pt() : -mother.pt();
3748+
if (absoluteParticlePdg == He3PDG) {
3749+
mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality);
3750+
mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality);
3751+
} else {
3752+
mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality);
3753+
mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality);
3754+
}
3755+
break;
3756+
}
3757+
}
3758+
}
3759+
36903760
// The detailed histogram is always filled for a truth-PDG-matched track,
36913761
// including wrong-collision associations, so tighter choices can be made
36923762
// offline without rerunning the table producer.
@@ -3771,15 +3841,93 @@ struct HadNucleiFemto {
37713841
}
37723842
}
37733843

3844+
using SelectedMcCollisionInfo = std::unordered_map<int64_t, std::pair<int, float>>;
3845+
3846+
void fillMCGeneratorNucleiFeedDown(const aod::McParticles& mcParticles, const SelectedMcCollisionInfo& selectedMcCollisions)
3847+
{
3848+
for (const auto& particle : mcParticles) {
3849+
const auto collision = selectedMcCollisions.find(particle.mcCollisionId());
3850+
if (collision == selectedMcCollisions.end()) {
3851+
continue;
3852+
}
3853+
const float centrality = collision->second.second;
3854+
const int particlePdg = particle.pdgCode();
3855+
const int absolutePdg = std::abs(particlePdg);
3856+
const float signedPt = particlePdg > 0 ? particle.pt() : -particle.pt();
3857+
3858+
if (absolutePdg == He3PDG && particle.isPhysicalPrimary()) {
3859+
mQaRegistry.fill(HIST("fractionMCGen/hPrimaryHe3"), signedPt, particle.y(), centrality);
3860+
continue;
3861+
}
3862+
if (absolutePdg == TritonPDG && particle.isPhysicalPrimary()) {
3863+
mQaRegistry.fill(HIST("fractionMCGen/hPrimaryTriton"), signedPt, particle.y(), centrality);
3864+
continue;
3865+
}
3866+
if (absolutePdg != HyperTritonPDG || !particle.isPhysicalPrimary()) {
3867+
continue;
3868+
}
3869+
3870+
mQaRegistry.fill(HIST("fractionMCGen/hHypertriton"), signedPt, particle.y(), centrality);
3871+
3872+
const int expectedHe3Pdg = particlePdg > 0 ? He3PDG : -He3PDG;
3873+
const int expectedTritonPdg = particlePdg > 0 ? TritonPDG : -TritonPDG;
3874+
const int expectedChargedPionPdg = particlePdg > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus;
3875+
bool hasExpectedHe3 = false;
3876+
bool hasExpectedTriton = false;
3877+
bool hasExpectedChargedPion = false;
3878+
bool hasPi0 = false;
3879+
float signedHe3Pt = 0.f;
3880+
float he3Rapidity = 0.f;
3881+
float signedTritonPt = 0.f;
3882+
float tritonRapidity = 0.f;
3883+
for (const auto& daughter : particle.template daughters_as<aod::McParticles>()) {
3884+
if (daughter.pdgCode() == expectedHe3Pdg) {
3885+
hasExpectedHe3 = true;
3886+
signedHe3Pt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt();
3887+
he3Rapidity = daughter.y();
3888+
} else if (daughter.pdgCode() == expectedTritonPdg) {
3889+
hasExpectedTriton = true;
3890+
signedTritonPt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt();
3891+
tritonRapidity = daughter.y();
3892+
} else if (daughter.pdgCode() == expectedChargedPionPdg) {
3893+
hasExpectedChargedPion = true;
3894+
} else if (daughter.pdgCode() == PDG_t::kPi0) {
3895+
hasPi0 = true;
3896+
}
3897+
}
3898+
3899+
if (hasExpectedHe3 && hasExpectedChargedPion) {
3900+
mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToHe3Pi"), signedPt, particle.y(), centrality);
3901+
mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterHe3"), signedHe3Pt, he3Rapidity, centrality);
3902+
mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterHe3Pt"), signedPt, signedHe3Pt, particle.y(), he3Rapidity, centrality);
3903+
}
3904+
if (hasExpectedTriton && hasPi0) {
3905+
mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToTritonPi0"), signedPt, particle.y(), centrality);
3906+
mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterTriton"), signedTritonPt, tritonRapidity, centrality);
3907+
mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterTritonPt"), signedPt, signedTritonPt, particle.y(), tritonRapidity, centrality);
3908+
}
3909+
}
3910+
}
3911+
37743912
// MC DCA templates use the relaxed-DCA selection. MC purity uses the full
37753913
// nominal candidate selection, including its DCA requirement.
3776-
void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles&, const aod::BCsWithTimestamps& bcs)
3914+
void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles& mcParticles, const aod::BCsWithTimestamps& bcs)
37773915
{
3916+
SelectedMcCollisionInfo selectedMcCollisions;
37783917
for (const auto& collision : collisions) {
37793918
if (!selectCollision</*isMC*/ true>(collision, bcs)) {
37803919
continue;
37813920
}
37823921

3922+
if (collision.has_mcCollision()) {
3923+
const int64_t mcCollisionId = collision.mcCollisionId();
3924+
const int numberOfContributors = collision.numContrib();
3925+
const auto storedCollision = selectedMcCollisions.find(mcCollisionId);
3926+
if (storedCollision == selectedMcCollisions.end() || numberOfContributors > storedCollision->second.first) {
3927+
selectedMcCollisions[mcCollisionId] = {numberOfContributors, collision.centFT0C()};
3928+
}
3929+
}
3930+
37833931
const uint64_t collIdx = collision.globalIndex();
37843932
auto tracksThisCollision = tracks.sliceBy(mPerColMC, collIdx);
37853933
tracksThisCollision.bindExternalIndices(&tracks);
@@ -3840,6 +3988,7 @@ struct HadNucleiFemto {
38403988
}
38413989
}
38423990
}
3991+
fillMCGeneratorNucleiFeedDown(mcParticles, selectedMcCollisions);
38433992
}
38443993
PROCESS_SWITCH(HadNucleiFemto, processDcaFractionPurityMC, "Produce MC DCA templates and truth-purity counters", false);
38453994

0 commit comments

Comments
 (0)