|
13 | 13 | /// \author sourav kundu |
14 | 14 | /// \since 02/11/2023 |
15 | 15 |
|
| 16 | +#include "PWGLF/DataModel/ReducedDoublePhiTables.h" |
| 17 | + |
| 18 | +#include "Common/Core/trackUtilities.h" |
| 19 | + |
| 20 | +#include "CommonConstants/PhysicsConstants.h" |
| 21 | +#include "Framework/ASoAHelpers.h" |
| 22 | +#include "Framework/AnalysisDataModel.h" |
| 23 | +#include "Framework/AnalysisTask.h" |
| 24 | +#include "Framework/StepTHn.h" |
| 25 | +#include "Framework/runDataProcessing.h" |
16 | 26 | #include <Framework/Configurable.h> |
17 | | -#include <TLorentzVector.h> |
| 27 | + |
18 | 28 | #include <Math/GenVector/Boost.h> |
19 | | -#include <Math/Vector4D.h> |
20 | 29 | #include <Math/Vector3D.h> |
| 30 | +#include <Math/Vector4D.h> |
| 31 | +#include <TLorentzVector.h> |
21 | 32 | #include <TMath.h> |
| 33 | +#include <TVector2.h> |
| 34 | + |
22 | 35 | #include <fairlogger/Logger.h> |
| 36 | + |
23 | 37 | #include <iostream> |
24 | 38 | #include <iterator> |
25 | 39 | #include <string> |
26 | 40 | #include <vector> |
27 | 41 |
|
28 | | -#include "Framework/AnalysisTask.h" |
29 | | -#include "Framework/ASoAHelpers.h" |
30 | | -#include "Framework/runDataProcessing.h" |
31 | | -#include "Framework/AnalysisDataModel.h" |
32 | | -#include "Framework/StepTHn.h" |
33 | | -#include "Common/Core/trackUtilities.h" |
34 | | -#include "PWGLF/DataModel/ReducedDoublePhiTables.h" |
35 | | -#include "CommonConstants/PhysicsConstants.h" |
36 | | - |
37 | 42 | using namespace o2; |
38 | 43 | using namespace o2::framework; |
39 | 44 | using namespace o2::framework::expressions; |
@@ -631,6 +636,207 @@ struct doublephimeson { |
631 | 636 | } |
632 | 637 | PROCESS_SWITCH(doublephimeson, processopti, "Process Optimized same event", false); |
633 | 638 |
|
| 639 | + void processopti2(aod::RedPhiEvents::iterator const& collision, aod::PhiTracks const& phitracks) |
| 640 | + { |
| 641 | + if (additionalEvsel && (collision.numPos() < 2 || collision.numNeg() < 2)) { |
| 642 | + return; |
| 643 | + } |
| 644 | + |
| 645 | + // === Helpers === |
| 646 | + // PDG phi mass (centralized constant, easy to vary for systematics) |
| 647 | + constexpr double mPhiPDG = 1.019461; // GeV/c^2 |
| 648 | + |
| 649 | + // ΔR with proper Δφ wrapping (−π, π] |
| 650 | + const auto deltaR = [](double phi1, double eta1, double phi2, double eta2) { |
| 651 | + const double dphi = TVector2::Phi_mpi_pi(phi1 - phi2); |
| 652 | + const double deta = eta1 - eta2; |
| 653 | + return std::sqrt(dphi * dphi + deta * deta); |
| 654 | + }; |
| 655 | + |
| 656 | + // Combined mass offset from PDG (tie-breaker metric) |
| 657 | + const auto deltam = [=](double m1, double m2) { |
| 658 | + return std::sqrt((m1 - mPhiPDG) * (m1 - mPhiPDG) + (m2 - mPhiPDG) * (m2 - mPhiPDG)); |
| 659 | + }; |
| 660 | + |
| 661 | + // Anti-merging/duplication veto: |
| 662 | + // Require same-sign kaons from the two φ's to be separated in (η,φ). |
| 663 | + // This protects against split/merged tracks and near-duplicate topologies. |
| 664 | + const auto passDaughterDR = [&](const ROOT::Math::PtEtaPhiMVector& kplusA, |
| 665 | + const ROOT::Math::PtEtaPhiMVector& kplusB, |
| 666 | + const ROOT::Math::PtEtaPhiMVector& kminusA, |
| 667 | + const ROOT::Math::PtEtaPhiMVector& kminusB) { |
| 668 | + const double dRkplus = deltaR(kplusA.Phi(), kplusA.Eta(), kplusB.Phi(), kplusB.Eta()); |
| 669 | + const double dRkminus = deltaR(kminusA.Phi(), kminusA.Eta(), kminusB.Phi(), kminusB.Eta()); |
| 670 | + return (dRkplus > daughterDeltaR) && (dRkminus > daughterDeltaR); |
| 671 | + // If later needed, make pT-aware: |
| 672 | + // const double thr = std::max(0.01, daughterDeltaR - 0.002 * std::min(10.0, exoticPt)); |
| 673 | + // return (dRkplus > thr) && (dRkminus > thr); |
| 674 | + }; |
| 675 | + |
| 676 | + // === Phi multiplicity (uses PID1 for d1, PID2 for d2) === |
| 677 | + int phimult = 0; |
| 678 | + for (auto const& t : phitracks) { |
| 679 | + if (t.phiMass() < minPhiMass1 || t.phiMass() > maxPhiMass1) |
| 680 | + continue; |
| 681 | + const double kpluspt = std::hypot(t.phid1Px(), t.phid1Py()); |
| 682 | + const double kminuspt = std::hypot(t.phid2Px(), t.phid2Py()); |
| 683 | + if (kpluspt > maxKaonPt) |
| 684 | + continue; |
| 685 | + if (kminuspt > maxKaonPt) |
| 686 | + continue; |
| 687 | + if (!selectionPID(t.phid1TPC(), t.phid1TOF(), t.phid1TOFHit(), strategyPID1, kpluspt)) |
| 688 | + continue; // d1 → PID1 |
| 689 | + if (!selectionPID(t.phid2TPC(), t.phid2TOF(), t.phid2TOFHit(), strategyPID2, kminuspt)) |
| 690 | + continue; // d2 → PID2 |
| 691 | + ++phimult; |
| 692 | + } |
| 693 | + |
| 694 | + // === Collect candidates first === |
| 695 | + std::vector<ROOT::Math::PtEtaPhiMVector> exoticres, phi1v, phi2v, kplus1, kplus2, kminus1, kminus2; |
| 696 | + std::vector<int> d1id, d2id, d3id, d4id; |
| 697 | + |
| 698 | + const auto n = phitracks.size(); |
| 699 | + exoticres.reserve(n); |
| 700 | + phi1v.reserve(n); |
| 701 | + phi2v.reserve(n); |
| 702 | + kplus1.reserve(n); |
| 703 | + kplus2.reserve(n); |
| 704 | + kminus1.reserve(n); |
| 705 | + kminus2.reserve(n); |
| 706 | + d1id.reserve(n); |
| 707 | + d2id.reserve(n); |
| 708 | + d3id.reserve(n); |
| 709 | + d4id.reserve(n); |
| 710 | + |
| 711 | + for (auto const& t1 : phitracks) { |
| 712 | + // Per-φ selection for the first φ |
| 713 | + const double kplus1pt = std::hypot(t1.phid1Px(), t1.phid1Py()); |
| 714 | + const double kminus1pt = std::hypot(t1.phid2Px(), t1.phid2Py()); |
| 715 | + if (kplus1pt > maxKaonPt) |
| 716 | + continue; |
| 717 | + if (kminus1pt > maxKaonPt) |
| 718 | + continue; |
| 719 | + if (!selectionPID(t1.phid1TPC(), t1.phid1TOF(), t1.phid1TOFHit(), strategyPID1, kplus1pt)) |
| 720 | + continue; |
| 721 | + if (!selectionPID(t1.phid2TPC(), t1.phid2TOF(), t1.phid2TOFHit(), strategyPID2, kminus1pt)) |
| 722 | + continue; |
| 723 | + |
| 724 | + // Set vectors BEFORE QA fills |
| 725 | + Phid1.SetXYZM(t1.phiPx(), t1.phiPy(), t1.phiPz(), t1.phiMass()); |
| 726 | + Phi1kaonplus.SetXYZM(t1.phid1Px(), t1.phid1Py(), t1.phid1Pz(), 0.493); |
| 727 | + Phi1kaonminus.SetXYZM(t1.phid2Px(), t1.phid2Py(), t1.phid2Pz(), 0.493); |
| 728 | + |
| 729 | + // PID QA |
| 730 | + histos.fill(HIST("hnsigmaTPCTOFKaon"), t1.phid1TPC(), t1.phid1TOF(), kplus1pt); |
| 731 | + histos.fill(HIST("hnsigmaTPCKaonPlus"), t1.phid1TPC(), kplus1pt); |
| 732 | + histos.fill(HIST("hnsigmaTPCKaonMinus"), t1.phid2TPC(), kminus1pt); |
| 733 | + histos.fill(HIST("hPhiMass"), Phid1.M(), Phid1.Pt()); |
| 734 | + |
| 735 | + const auto id1 = t1.index(); |
| 736 | + |
| 737 | + for (auto const& t2 : phitracks) { |
| 738 | + const auto id2 = t2.index(); |
| 739 | + if (id2 <= id1) |
| 740 | + continue; // unique unordered pairs |
| 741 | + |
| 742 | + // Per-φ selection for the second φ |
| 743 | + const double kplus2pt = std::hypot(t2.phid1Px(), t2.phid1Py()); |
| 744 | + const double kminus2pt = std::hypot(t2.phid2Px(), t2.phid2Py()); |
| 745 | + if (kplus2pt > maxKaonPt) |
| 746 | + continue; |
| 747 | + if (kminus2pt > maxKaonPt) |
| 748 | + continue; |
| 749 | + if (!selectionPID(t2.phid1TPC(), t2.phid1TOF(), t2.phid1TOFHit(), strategyPID1, kplus2pt)) |
| 750 | + continue; |
| 751 | + if (!selectionPID(t2.phid2TPC(), t2.phid2TOF(), t2.phid2TOFHit(), strategyPID2, kminus2pt)) |
| 752 | + continue; |
| 753 | + |
| 754 | + // Disallow shared same-sign daughters between the two φ's |
| 755 | + if ((t1.phid1Index() == t2.phid1Index()) || (t1.phid2Index() == t2.phid2Index())) |
| 756 | + continue; |
| 757 | + |
| 758 | + Phid2.SetXYZM(t2.phiPx(), t2.phiPy(), t2.phiPz(), t2.phiMass()); |
| 759 | + Phi2kaonplus.SetXYZM(t2.phid1Px(), t2.phid1Py(), t2.phid1Pz(), 0.493); |
| 760 | + Phi2kaonminus.SetXYZM(t2.phid2Px(), t2.phid2Py(), t2.phid2Pz(), 0.493); |
| 761 | + |
| 762 | + // Mass windows |
| 763 | + if (t1.phiMass() < minPhiMass1 || t1.phiMass() > maxPhiMass1) |
| 764 | + continue; |
| 765 | + if (t2.phiMass() < minPhiMass2 || t2.phiMass() > maxPhiMass2) |
| 766 | + continue; |
| 767 | + |
| 768 | + exotic = Phid1 + Phid2; |
| 769 | + if (exotic.M() < minExoticMass || exotic.M() > maxExoticMass) |
| 770 | + continue; |
| 771 | + |
| 772 | + // Store candidate and bookkeeping |
| 773 | + exoticres.emplace_back(exotic.Pt(), exotic.Eta(), exotic.Phi(), exotic.M()); |
| 774 | + phi1v.emplace_back(Phid1.Pt(), Phid1.Eta(), Phid1.Phi(), Phid1.M()); |
| 775 | + phi2v.emplace_back(Phid2.Pt(), Phid2.Eta(), Phid2.Phi(), Phid2.M()); |
| 776 | + |
| 777 | + kplus1.emplace_back(Phi1kaonplus.Pt(), Phi1kaonplus.Eta(), Phi1kaonplus.Phi(), 0.493); |
| 778 | + kminus1.emplace_back(Phi1kaonminus.Pt(), Phi1kaonminus.Eta(), Phi1kaonminus.Phi(), 0.493); |
| 779 | + kplus2.emplace_back(Phi2kaonplus.Pt(), Phi2kaonplus.Eta(), Phi2kaonplus.Phi(), 0.493); |
| 780 | + kminus2.emplace_back(Phi2kaonminus.Pt(), Phi2kaonminus.Eta(), Phi2kaonminus.Phi(), 0.493); |
| 781 | + |
| 782 | + d1id.push_back(t1.phid1Index()); |
| 783 | + d2id.push_back(t2.phid1Index()); |
| 784 | + d3id.push_back(t1.phid2Index()); |
| 785 | + d4id.push_back(t2.phid2Index()); |
| 786 | + } |
| 787 | + } |
| 788 | + |
| 789 | + if (exoticres.empty()) |
| 790 | + return; |
| 791 | + |
| 792 | + // === Special handling if exactly two candidates were built === |
| 793 | + if (exoticres.size() == 2) { |
| 794 | + const int i0 = 0, i1 = 1; |
| 795 | + |
| 796 | + const bool sameFour = |
| 797 | + ((d1id[i0] == d1id[i1] || d1id[i0] == d2id[i1]) && |
| 798 | + (d2id[i0] == d1id[i1] || d2id[i0] == d2id[i1]) && |
| 799 | + (d3id[i0] == d3id[i1] || d3id[i0] == d4id[i1]) && |
| 800 | + (d4id[i0] == d3id[i1] || d4id[i0] == d4id[i1])); |
| 801 | + |
| 802 | + const double deltam0 = deltam(phi1v[i0].M(), phi2v[i0].M()); |
| 803 | + const double deltam1 = deltam(phi1v[i1].M(), phi2v[i1].M()); |
| 804 | + const bool dr0 = passDaughterDR(kplus1[i0], kplus2[i0], kminus1[i0], kminus2[i0]); |
| 805 | + const bool dr1 = passDaughterDR(kplus1[i1], kplus2[i1], kminus1[i1], kminus2[i1]); |
| 806 | + |
| 807 | + if (sameFour) { |
| 808 | + // Choose the candidate closer to mφ (if it passes ΔR) |
| 809 | + int keep = (deltam1 < deltam0 && dr1) ? i1 : (dr0 ? i0 : -1); |
| 810 | + if (keep >= 0) { |
| 811 | + const double dR = deltaR(phi1v[keep].Phi(), phi1v[keep].Eta(), phi2v[keep].Phi(), phi2v[keep].Eta()); |
| 812 | + const double dm = (keep == i0) ? deltam0 : deltam1; |
| 813 | + histos.fill(HIST("SEMassUnlike"), exoticres[keep].M(), exoticres[keep].Pt(), dR, dm, phimult); |
| 814 | + } |
| 815 | + } else { |
| 816 | + // Independent candidates → fill both (respect ΔR) |
| 817 | + if (dr0) { |
| 818 | + const double dR0 = deltaR(phi1v[i0].Phi(), phi1v[i0].Eta(), phi2v[i0].Phi(), phi2v[i0].Eta()); |
| 819 | + histos.fill(HIST("SEMassUnlike"), exoticres[i0].M(), exoticres[i0].Pt(), dR0, deltam0, phimult); |
| 820 | + } |
| 821 | + if (dr1) { |
| 822 | + const double dR1 = deltaR(phi1v[i1].Phi(), phi1v[i1].Eta(), phi2v[i1].Phi(), phi2v[i1].Eta()); |
| 823 | + histos.fill(HIST("SEMassUnlike"), exoticres[i1].M(), exoticres[i1].Pt(), dR1, deltam1, phimult); |
| 824 | + } |
| 825 | + } |
| 826 | + return; |
| 827 | + } |
| 828 | + |
| 829 | + // === General case: fill each candidate once (respect ΔR) === |
| 830 | + for (size_t i = 0; i < exoticres.size(); ++i) { |
| 831 | + if (!passDaughterDR(kplus1[i], kplus2[i], kminus1[i], kminus2[i])) |
| 832 | + continue; |
| 833 | + const double dR = deltaR(phi1v[i].Phi(), phi1v[i].Eta(), phi2v[i].Phi(), phi2v[i].Eta()); |
| 834 | + const double dm = deltam(phi1v[i].M(), phi2v[i].M()); |
| 835 | + histos.fill(HIST("SEMassUnlike"), exoticres[i].M(), exoticres[i].Pt(), dR, dm, phimult); |
| 836 | + } |
| 837 | + } |
| 838 | + PROCESS_SWITCH(doublephimeson, processopti2, "Process Optimized same event", false); |
| 839 | + |
634 | 840 | SliceCache cache; |
635 | 841 | using BinningTypeVertexContributor = ColumnBinningPolicy<aod::collision::PosZ, aod::collision::NumContrib>; |
636 | 842 | void processMixedEvent(aod::RedPhiEvents& collisions, aod::PhiTracks& phitracks) |
|
0 commit comments