From f559dbad229f59754df9451b6c0f552b307fc263 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Mon, 5 Oct 2026 22:06:55 +0200 Subject: [PATCH 1/5] Update HadNucleiFemto.cxx --- .../TableProducer/HadNucleiFemto.cxx | 151 +++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index cafbb4acc3a..958cedfb72b 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -768,6 +768,28 @@ struct HadNucleiFemto { {"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(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, {"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(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, + // Generator denominators and source-resolved response matrices for the + // hypertriton-to-primary He3 and triton constraints. Generated momenta + // are physical particle momenta and must never receive the reconstructed + // He3-track x2 rigidity conversion. The two-body histograms are + // conditional on their generated decay channel, so the physical + // branching ratios remain independent offline inputs. + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + {"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}}}}, + // Hierarchical MC truth purity counters: all = correct species + mis-ID // + no label; correct species = correct collision + wrong collision; // correct collision = primary + weak + material. @@ -3687,6 +3709,54 @@ struct HadNucleiFemto { const float productionRadius = std::hypot(particle.vx(), particle.vy()); const float signedPt = signedPhysicalPt(track, isNucleus); + // These response matrices use the relaxed DCA-fit selection under which + // this function is called. Keep only correct-collision He3 or triton so + // the reconstructed numerators match the selected-MC-collision generator + // denominators filled below. Hypertriton responses are conditional on the + // explicit He3-pion or triton-pi0 channel; physical branching ratios are + // deliberately not folded into these histograms. + const int absoluteParticlePdg = std::abs(particle.pdgCode()); + if (isNucleus && matchesRecoCollision && (absoluteParticlePdg == He3PDG || absoluteParticlePdg == TritonPDG)) { + const float signedGeneratedPt = particle.pdgCode() > 0 ? particle.pt() : -particle.pt(); + const float generatedRapidity = particle.y(); + if (origin == Primary) { + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } + } else if (origin == WeakDecay && particle.has_mothers()) { + for (const auto& mother : particle.template mothers_as()) { + if (std::abs(mother.pdgCode()) != HyperTritonPDG || !mother.isPhysicalPrimary() || + ((mother.pdgCode() > 0) != (particle.pdgCode() > 0))) { + continue; + } + const int expectedPionPdg = absoluteParticlePdg == He3PDG + ? (mother.pdgCode() > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus) + : PDG_t::kPi0; + bool hasExpectedPion = false; + for (const auto& daughter : mother.template daughters_as()) { + if (daughter.pdgCode() == expectedPionPdg) { + hasExpectedPion = true; + break; + } + } + if (!hasExpectedPion) { + continue; + } + const float signedParentPt = mother.pdgCode() > 0 ? mother.pt() : -mother.pt(); + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } + break; + } + } + } + // The detailed histogram is always filled for a truth-PDG-matched track, // including wrong-collision associations, so tighter choices can be made // offline without rerunning the table producer. @@ -3771,15 +3841,93 @@ struct HadNucleiFemto { } } + using SelectedMcCollisionInfo = std::unordered_map>; + + void fillMCGeneratorNucleiFeedDown(const aod::McParticles& mcParticles, const SelectedMcCollisionInfo& selectedMcCollisions) + { + for (const auto& particle : mcParticles) { + const auto collision = selectedMcCollisions.find(particle.mcCollisionId()); + if (collision == selectedMcCollisions.end()) { + continue; + } + const float centrality = collision->second.second; + const int particlePdg = particle.pdgCode(); + const int absolutePdg = std::abs(particlePdg); + const float signedPt = particlePdg > 0 ? particle.pt() : -particle.pt(); + + if (absolutePdg == He3PDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryHe3"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg == TritonPDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryTriton"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg != HyperTritonPDG || !particle.isPhysicalPrimary()) { + continue; + } + + mQaRegistry.fill(HIST("fractionMCGen/hHypertriton"), signedPt, particle.y(), centrality); + + const int expectedHe3Pdg = particlePdg > 0 ? He3PDG : -He3PDG; + const int expectedTritonPdg = particlePdg > 0 ? TritonPDG : -TritonPDG; + const int expectedChargedPionPdg = particlePdg > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus; + bool hasExpectedHe3 = false; + bool hasExpectedTriton = false; + bool hasExpectedChargedPion = false; + bool hasPi0 = false; + float signedHe3Pt = 0.f; + float he3Rapidity = 0.f; + float signedTritonPt = 0.f; + float tritonRapidity = 0.f; + for (const auto& daughter : particle.template daughters_as()) { + if (daughter.pdgCode() == expectedHe3Pdg) { + hasExpectedHe3 = true; + signedHe3Pt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + he3Rapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedTritonPdg) { + hasExpectedTriton = true; + signedTritonPt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + tritonRapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedChargedPionPdg) { + hasExpectedChargedPion = true; + } else if (daughter.pdgCode() == PDG_t::kPi0) { + hasPi0 = true; + } + } + + if (hasExpectedHe3 && hasExpectedChargedPion) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToHe3Pi"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterHe3"), signedHe3Pt, he3Rapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterHe3Pt"), signedPt, signedHe3Pt, particle.y(), he3Rapidity, centrality); + } + if (hasExpectedTriton && hasPi0) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToTritonPi0"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterTriton"), signedTritonPt, tritonRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterTritonPt"), signedPt, signedTritonPt, particle.y(), tritonRapidity, centrality); + } + } + } + // MC DCA templates use the relaxed-DCA selection. MC purity uses the full // nominal candidate selection, including its DCA requirement. - void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) + void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles& mcParticles, const aod::BCsWithTimestamps& bcs) { + SelectedMcCollisionInfo selectedMcCollisions; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; } + if (collision.has_mcCollision()) { + const int64_t mcCollisionId = collision.mcCollisionId(); + const int numberOfContributors = collision.numContrib(); + const auto storedCollision = selectedMcCollisions.find(mcCollisionId); + if (storedCollision == selectedMcCollisions.end() || numberOfContributors > storedCollision->second.first) { + selectedMcCollisions[mcCollisionId] = {numberOfContributors, collision.centFT0C()}; + } + } + const uint64_t collIdx = collision.globalIndex(); auto tracksThisCollision = tracks.sliceBy(mPerColMC, collIdx); tracksThisCollision.bindExternalIndices(&tracks); @@ -3840,6 +3988,7 @@ struct HadNucleiFemto { } } } + fillMCGeneratorNucleiFeedDown(mcParticles, selectedMcCollisions); } PROCESS_SWITCH(HadNucleiFemto, processDcaFractionPurityMC, "Produce MC DCA templates and truth-purity counters", false); From 70c0dc24cf875ad399376d661bccd535d3b33738 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Mon, 5 Oct 2026 22:43:41 +0200 Subject: [PATCH 2/5] Update HadNucleiFemto.cxx --- PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 958cedfb72b..bb266f0bdf8 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -11,7 +11,7 @@ // /// \file HadNucleiFemtoDcaPurity.cxx -/// \brief Nuclei-hadron femtoscopy task with DCA-fraction and purity inputs +/// \brief Nuclei-hadron femtoscopy task /// \author CMY /// \date 2025-04-10 From 11863b9026991f9532963f37eb98b868dcf35506 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Mon, 5 Oct 2026 23:00:22 +0200 Subject: [PATCH 3/5] Update HadNucleiFemto.cxx --- PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index bb266f0bdf8..65f64d22870 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -11,7 +11,7 @@ // /// \file HadNucleiFemtoDcaPurity.cxx -/// \brief Nuclei-hadron femtoscopy task +/// \brief Nuclei-hadron femtoscopy task with DCAfit /// \author CMY /// \date 2025-04-10 From 29bf6949b07279580f7614d82359d605a9ecc9a9 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Mon, 5 Oct 2026 23:18:26 +0200 Subject: [PATCH 4/5] Update HadNucleiFemto.cxx --- PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 65f64d22870..182df0664cb 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -11,7 +11,7 @@ // /// \file HadNucleiFemtoDcaPurity.cxx -/// \brief Nuclei-hadron femtoscopy task with DCAfit +/// \brief Nuclei-hadron femtoscopy task /// \author CMY /// \date 2025-04-10 From 50f5e97f212ba2222ae3bcece8cfd9e956b6b776 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 5 Oct 2026 21:19:29 +0000 Subject: [PATCH 5/5] Please consider the following formatting changes --- PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 182df0664cb..bb266f0bdf8 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -11,7 +11,7 @@ // /// \file HadNucleiFemtoDcaPurity.cxx -/// \brief Nuclei-hadron femtoscopy task +/// \brief Nuclei-hadron femtoscopy task /// \author CMY /// \date 2025-04-10