|
20 | 20 | #include "PWGJE/Core/JetUtilities.h" |
21 | 21 | #include "PWGJE/DataModel/Jet.h" |
22 | 22 | #include "PWGJE/DataModel/JetReducedData.h" |
| 23 | +#include "PWGLF/DataModel/mcCentrality.h" |
23 | 24 |
|
24 | 25 | #include "Common/Core/TrackSelection.h" |
25 | 26 | #include "Common/Core/Zorro.h" |
|
34 | 35 |
|
35 | 36 | #include "CCDB/BasicCCDBManager.h" |
36 | 37 | #include "CCDB/CcdbApi.h" |
37 | | -#include "MathUtils/BetheBlochAleph.h" |
38 | 38 | #include "Framework/ASoA.h" |
39 | 39 | #include "Framework/ASoAHelpers.h" |
40 | 40 | #include "Framework/AnalysisDataModel.h" |
|
44 | 44 | #include "Framework/Logger.h" |
45 | 45 | #include "Framework/RunningWorkflowInfo.h" |
46 | 46 | #include "Framework/runDataProcessing.h" |
| 47 | +#include "MathUtils/BetheBlochAleph.h" |
47 | 48 | #include "ReconstructionDataFormats/DCA.h" |
48 | 49 | #include "ReconstructionDataFormats/PID.h" |
49 | 50 | #include "ReconstructionDataFormats/Track.h" |
@@ -89,10 +90,9 @@ using std::array; |
89 | 90 | // Define convenient aliases for commonly used table joins |
90 | 91 | using SelectedCollisions = soa::Join<aod::Collisions, aod::EvSels, aod::CentFT0Ms>; |
91 | 92 | using RecCollisionsMc = soa::Join<aod::Collisions, aod::EvSels, aod::CentFT0Ms, aod::McCollisionLabels>; |
92 | | -using GenCollisionsMc = aod::McCollisions; |
| 93 | +using GenCollisionsMc = soa::Join<aod::McCollisions, aod::McCentFT0Ms>; |
93 | 94 | using AntiNucleiTracks = soa::Join<aod::Tracks, aod::TracksExtra, aod::TrackSelection, aod::TrackSelectionExtension, aod::TracksDCA, aod::pidTPCFullPr, aod::pidTPCFullDe, aod::pidTPCFullHe, aod::pidTOFFullPr, aod::pidTOFFullDe, aod::pidTOFFullHe>; |
94 | 95 | using AntiNucleiTracksMc = soa::Join<AntiNucleiTracks, aod::McTrackLabels>; |
95 | | - |
96 | 96 | using LorentzVector = ROOT::Math::PxPyPzEVector; |
97 | 97 |
|
98 | 98 | // Lightweight particle container for fast kinematic access |
@@ -489,6 +489,38 @@ struct AntinucleiInJets { |
489 | 489 | registryMC.add("antiproton_coal_ue", "antiproton_coal_ue", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); |
490 | 490 | } |
491 | 491 |
|
| 492 | + // Coalescence and Correlation analysis |
| 493 | + if (doprocessCoalescenceCorr) { |
| 494 | + |
| 495 | + // Axes definitions for multidimensional histogram binning |
| 496 | + const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; |
| 497 | + const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; |
| 498 | + const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; |
| 499 | + const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; |
| 500 | + const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; |
| 501 | + const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; |
| 502 | + const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; |
| 503 | + |
| 504 | + registryMC.add("genEventsCoalescenceCorr", "genEventsCoalescenceCorr", HistType::kTH1F, {{20, 0, 20, "counter"}}); |
| 505 | + registryMC.add("antideuteron_fullEvent_CoalescenceCorr", "antideuteron_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); |
| 506 | + registryMC.add("antiproton_fullEvent_CoalescenceCorr", "antiproton_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); |
| 507 | + |
| 508 | + // Counter histograms |
| 509 | + registryCorr.add("eventCounter_CoalescenceCorr", "number of events in Coalescence simulation", HistType::kTH1F, {{20, 0, 20, "counter"}}); |
| 510 | + registryCorr.add("eventCounter_centrality_fullEvent_CoalescenceCorr", "Number of events per centrality (Full Event) in Coalescence simulation", HistType::kTH1F, {multiplicityAxis}); |
| 511 | + |
| 512 | + // Correlation histograms |
| 513 | + registryCorr.add("rho_fullEvent_CoalescenceCorr", "rho_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); |
| 514 | + registryCorr.add("rho_netP_netD_fullEvent_CoalescenceCorr", "rho_netP_netD_fullEvent_CoalescenceCorr", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); |
| 515 | + |
| 516 | + // Efficiency histograms full event |
| 517 | + registryCorr.add("q1d_fullEvent_CoalescenceCorr", "q1d_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); |
| 518 | + registryCorr.add("q1p_fullEvent_CoalescenceCorr", "q1p_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); |
| 519 | + registryCorr.add("q1d_square_fullEvent_CoalescenceCorr", "q1d_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); |
| 520 | + registryCorr.add("q1p_square_fullEvent_CoalescenceCorr", "q1p_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); |
| 521 | + registryCorr.add("q1d_q1p_fullEvent_CoalescenceCorr", "q1d_q1p_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); |
| 522 | + } |
| 523 | + |
492 | 524 | // Systematic uncertainties (Data) |
493 | 525 | if (doprocessSystData) { |
494 | 526 | registryData.add("number_of_events_data_syst", "event counter", HistType::kTH1F, {{20, 0, 20, "counter"}}); |
@@ -3597,6 +3629,209 @@ struct AntinucleiInJets { |
3597 | 3629 | } |
3598 | 3630 | } |
3599 | 3631 | PROCESS_SWITCH(AntinucleiInJets, processCoalescence, "process coalescence", false); |
| 3632 | + |
| 3633 | + // process Coalescence and Correlation Analysis |
| 3634 | + void processCoalescenceCorr(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) |
| 3635 | + { |
| 3636 | + // Deuteron Mass and minimum pt |
| 3637 | + double massDeut = o2::constants::physics::MassDeuteron; |
| 3638 | + static constexpr double MinPtPerNucleon = 0.1; // Cut on pt/A |
| 3639 | + |
| 3640 | + // Containers for candidates (before coalescence) |
| 3641 | + std::vector<ReducedParticle> protonCandidates; |
| 3642 | + std::vector<ReducedParticle> neutronCandidates; |
| 3643 | + |
| 3644 | + // Final containers for analysis (after coalescence) |
| 3645 | + std::vector<ReducedParticle> finalProtons; |
| 3646 | + std::vector<ReducedParticle> finalDeuterons; |
| 3647 | + |
| 3648 | + // pt/A bins |
| 3649 | + std::vector<double> ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; |
| 3650 | + const int nBins = ptOverAbins.size() - 1; |
| 3651 | + |
| 3652 | + // Loop over all simulated collisions |
| 3653 | + for (const auto& collision : collisions) { |
| 3654 | + |
| 3655 | + // Clear containers |
| 3656 | + protonCandidates.clear(); |
| 3657 | + neutronCandidates.clear(); |
| 3658 | + finalProtons.clear(); |
| 3659 | + finalDeuterons.clear(); |
| 3660 | + |
| 3661 | + // Event counter: before event selection |
| 3662 | + registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 0.5); |
| 3663 | + registryMC.fill(HIST("genEventsCoalescenceCorr"), 0.5); |
| 3664 | + |
| 3665 | + // Apply event selection: require vertex position to be within the allowed z range |
| 3666 | + if (std::fabs(collision.posZ()) > zVtx) |
| 3667 | + continue; |
| 3668 | + |
| 3669 | + // Event counter: after event selection |
| 3670 | + registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 1.5); |
| 3671 | + registryMC.fill(HIST("genEventsCoalescenceCorr"), 1.5); |
| 3672 | + |
| 3673 | + // Multiplicity percentile |
| 3674 | + const float multiplicity = collision.centFT0M(); |
| 3675 | + |
| 3676 | + // Fill event counter vs centrality |
| 3677 | + registryCorr.fill(HIST("eventCounter_centrality_fullEvent_CoalescenceCorr"), multiplicity); |
| 3678 | + |
| 3679 | + // Get particles in this MC collision |
| 3680 | + const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); |
| 3681 | + |
| 3682 | + // Loop over MC particles |
| 3683 | + for (const auto& particle : mcParticlesThisMcColl) { |
| 3684 | + |
| 3685 | + // Monte Carlo index |
| 3686 | + int mcId = particle.globalIndex(); |
| 3687 | + int pdg = particle.pdgCode(); |
| 3688 | + int absPdg = std::abs(pdg); |
| 3689 | + |
| 3690 | + // Store Protons |
| 3691 | + if (particle.isPhysicalPrimary()) { |
| 3692 | + if (absPdg == PDG_t::kProton) { |
| 3693 | + protonCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); |
| 3694 | + } else if (absPdg == PDG_t::kNeutron) { // Store Neutrons |
| 3695 | + neutronCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); |
| 3696 | + } |
| 3697 | + } |
| 3698 | + } |
| 3699 | + |
| 3700 | + // Reject empty events |
| 3701 | + if (protonCandidates.empty() && neutronCandidates.empty()) |
| 3702 | + continue; |
| 3703 | + |
| 3704 | + registryMC.fill(HIST("genEventsCoalescenceCorr"), 2.5); |
| 3705 | + |
| 3706 | + // Build deuterons |
| 3707 | + for (size_t iP = 0; iP < protonCandidates.size(); ++iP) { |
| 3708 | + if (protonCandidates[iP].used) |
| 3709 | + continue; |
| 3710 | + |
| 3711 | + for (size_t iN = 0; iN < neutronCandidates.size(); ++iN) { |
| 3712 | + if (neutronCandidates[iN].used) |
| 3713 | + continue; |
| 3714 | + |
| 3715 | + // Physics consistency check |
| 3716 | + if (protonCandidates[iP].pdgCode * neutronCandidates[iN].pdgCode < 0) |
| 3717 | + continue; |
| 3718 | + |
| 3719 | + if (passDeuteronCoalescence(protonCandidates[iP], neutronCandidates[iN], coalescenceMomentum, mRand)) { |
| 3720 | + neutronCandidates[iN].used = true; |
| 3721 | + protonCandidates[iP].used = true; |
| 3722 | + |
| 3723 | + int sign = (protonCandidates[iP].pdgCode > 0) ? +1 : -1; |
| 3724 | + int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; |
| 3725 | + |
| 3726 | + double pxDeut = protonCandidates[iP].px + neutronCandidates[iN].px; |
| 3727 | + double pyDeut = protonCandidates[iP].py + neutronCandidates[iN].py; |
| 3728 | + double pzDeut = protonCandidates[iP].pz + neutronCandidates[iN].pz; |
| 3729 | + double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); |
| 3730 | + LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); |
| 3731 | + if (pd.Eta() >= minEta && pd.Eta() <= maxEta && (0.5 * pd.Pt()) >= MinPtPerNucleon) { |
| 3732 | + // Store Deuteron |
| 3733 | + finalDeuterons.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, protonCandidates[iP].mcIndex, false}); |
| 3734 | + } |
| 3735 | + |
| 3736 | + break; |
| 3737 | + } |
| 3738 | + } |
| 3739 | + } |
| 3740 | + |
| 3741 | + // Add unused protons to final vectors |
| 3742 | + for (const auto& proton : protonCandidates) { |
| 3743 | + if (!proton.used) { |
| 3744 | + finalProtons.push_back(proton); |
| 3745 | + } |
| 3746 | + } |
| 3747 | + |
| 3748 | + // Correlation Analysis |
| 3749 | + std::vector<int> nAntiprotonFullEvent(nBins, 0); |
| 3750 | + std::vector<int> nAntideuteronFullEvent(nBins, 0); |
| 3751 | + int nTotProtonFullEvent(0); |
| 3752 | + int nTotDeuteronFullEvent(0); |
| 3753 | + int nTotAntiprotonFullEvent(0); |
| 3754 | + int nTotAntideuteronFullEvent(0); |
| 3755 | + |
| 3756 | + // Loop over final protons |
| 3757 | + for (const auto& part : finalProtons) { |
| 3758 | + double pt = std::hypot(part.px, part.py); |
| 3759 | + |
| 3760 | + if (part.eta() < minEta || part.eta() > maxEta) |
| 3761 | + continue; |
| 3762 | + |
| 3763 | + // Standard histograms for antiprotons |
| 3764 | + if (part.pdgCode == PDG_t::kProtonBar) { |
| 3765 | + registryMC.fill(HIST("antiproton_fullEvent_CoalescenceCorr"), pt); |
| 3766 | + } |
| 3767 | + |
| 3768 | + // Kinematic selection and Multiplicity counting |
| 3769 | + if (pt < ptOverAbins[0] || pt >= ptOverAbins[nBins]) |
| 3770 | + continue; |
| 3771 | + |
| 3772 | + if (part.pdgCode > 0) { |
| 3773 | + nTotProtonFullEvent++; |
| 3774 | + } else { |
| 3775 | + nTotAntiprotonFullEvent++; |
| 3776 | + int ibin = findBin(ptOverAbins, pt); |
| 3777 | + if (ibin >= 0 && ibin < nBins) |
| 3778 | + nAntiprotonFullEvent[ibin]++; |
| 3779 | + } |
| 3780 | + } |
| 3781 | + |
| 3782 | + // Loop over final deuterons |
| 3783 | + for (const auto& part : finalDeuterons) { |
| 3784 | + double pt = std::hypot(part.px, part.py); |
| 3785 | + double ptPerNucleon = 0.5 * pt; |
| 3786 | + |
| 3787 | + // Apply detector acceptance cuts (to match real data) |
| 3788 | + if (part.eta() < minEta || part.eta() > maxEta) |
| 3789 | + continue; |
| 3790 | + |
| 3791 | + // Standard histograms for antideuterons |
| 3792 | + if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { |
| 3793 | + registryMC.fill(HIST("antideuteron_fullEvent_CoalescenceCorr"), pt); |
| 3794 | + } |
| 3795 | + |
| 3796 | + // Kinematic selection and Multiplicity counting |
| 3797 | + if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) |
| 3798 | + continue; |
| 3799 | + |
| 3800 | + if (part.pdgCode > 0) { |
| 3801 | + nTotDeuteronFullEvent++; |
| 3802 | + } else { |
| 3803 | + nTotAntideuteronFullEvent++; |
| 3804 | + int ibin = findBin(ptOverAbins, ptPerNucleon); |
| 3805 | + if (ibin >= 0 && ibin < nBins) |
| 3806 | + nAntideuteronFullEvent[ibin]++; |
| 3807 | + } |
| 3808 | + } |
| 3809 | + |
| 3810 | + // Fill correlation histograms |
| 3811 | + int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; |
| 3812 | + int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; |
| 3813 | + |
| 3814 | + registryCorr.fill(HIST("rho_fullEvent_CoalescenceCorr"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity); |
| 3815 | + registryCorr.fill(HIST("rho_netP_netD_fullEvent_CoalescenceCorr"), netDeuteronFullEvent, netProtonFullEvent); |
| 3816 | + |
| 3817 | + // Fill efficiency histograms |
| 3818 | + for (int i = 0; i < nBins; i++) { |
| 3819 | + double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); |
| 3820 | + |
| 3821 | + registryCorr.fill(HIST("q1d_fullEvent_CoalescenceCorr"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity); |
| 3822 | + registryCorr.fill(HIST("q1p_fullEvent_CoalescenceCorr"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity); |
| 3823 | + |
| 3824 | + for (int j = 0; j < nBins; j++) { |
| 3825 | + double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); |
| 3826 | + |
| 3827 | + registryCorr.fill(HIST("q1d_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast<double>(nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity); |
| 3828 | + registryCorr.fill(HIST("q1p_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast<double>(nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); |
| 3829 | + registryCorr.fill(HIST("q1d_q1p_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast<double>(nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); |
| 3830 | + } |
| 3831 | + } |
| 3832 | + } |
| 3833 | + } |
| 3834 | + PROCESS_SWITCH(AntinucleiInJets, processCoalescenceCorr, "process coalescence correlation", false); |
3600 | 3835 | }; |
3601 | 3836 |
|
3602 | 3837 | WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) |
|
0 commit comments