|
11 | 11 | // |
12 | 12 |
|
13 | 13 | /// \file HadNucleiFemtoDcaPurity.cxx |
14 | | -/// \brief Nuclei-hadron femtoscopy task with DCA-fraction and purity inputs |
| 14 | +/// \brief Nuclei-hadron femtoscopy task |
15 | 15 | /// \author CMY |
16 | 16 | /// \date 2025-04-10 |
17 | 17 |
|
@@ -768,6 +768,28 @@ struct HadNucleiFemto { |
768 | 768 | {"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}}}}, |
769 | 769 | {"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}}}}, |
770 | 770 |
|
| 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 | + |
771 | 793 | // Hierarchical MC truth purity counters: all = correct species + mis-ID |
772 | 794 | // + no label; correct species = correct collision + wrong collision; |
773 | 795 | // correct collision = primary + weak + material. |
@@ -3687,6 +3709,54 @@ struct HadNucleiFemto { |
3687 | 3709 | const float productionRadius = std::hypot(particle.vx(), particle.vy()); |
3688 | 3710 | const float signedPt = signedPhysicalPt(track, isNucleus); |
3689 | 3711 |
|
| 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 | + |
3690 | 3760 | // The detailed histogram is always filled for a truth-PDG-matched track, |
3691 | 3761 | // including wrong-collision associations, so tighter choices can be made |
3692 | 3762 | // offline without rerunning the table producer. |
@@ -3771,15 +3841,93 @@ struct HadNucleiFemto { |
3771 | 3841 | } |
3772 | 3842 | } |
3773 | 3843 |
|
| 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 | + |
3774 | 3912 | // MC DCA templates use the relaxed-DCA selection. MC purity uses the full |
3775 | 3913 | // 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) |
3777 | 3915 | { |
| 3916 | + SelectedMcCollisionInfo selectedMcCollisions; |
3778 | 3917 | for (const auto& collision : collisions) { |
3779 | 3918 | if (!selectCollision</*isMC*/ true>(collision, bcs)) { |
3780 | 3919 | continue; |
3781 | 3920 | } |
3782 | 3921 |
|
| 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 | + |
3783 | 3931 | const uint64_t collIdx = collision.globalIndex(); |
3784 | 3932 | auto tracksThisCollision = tracks.sliceBy(mPerColMC, collIdx); |
3785 | 3933 | tracksThisCollision.bindExternalIndices(&tracks); |
@@ -3840,6 +3988,7 @@ struct HadNucleiFemto { |
3840 | 3988 | } |
3841 | 3989 | } |
3842 | 3990 | } |
| 3991 | + fillMCGeneratorNucleiFeedDown(mcParticles, selectedMcCollisions); |
3843 | 3992 | } |
3844 | 3993 | PROCESS_SWITCH(HadNucleiFemto, processDcaFractionPurityMC, "Produce MC DCA templates and truth-purity counters", false); |
3845 | 3994 |
|
|
0 commit comments