@@ -355,6 +355,7 @@ struct FemtoUniverseProducerTask {
355355 Configurable<float > confPhiKaonNsigmaTOFfrom055to15{" confPhiKaonNsigmaTOFfrom055to15" , 3.0 , " Reject if Kaons in 0.55-1.5 are have TOF n sigma above this value." };
356356 Configurable<float > confPhiKaonNsigmaTPCfrom15{" confPhiKaonNsigmaTPCfrom15" , 3.0 , " Reject if Kaons above 1.5 are have TPC n sigma above this value." };
357357 Configurable<float > confPhiKaonNsigmaTOFfrom15{" confPhiKaonNsigmaTOFfrom15" , 3.0 , " Reject if Kaons above 1.5 are have TOF n sigma above this value." };
358+ Configurable<bool > confDoLikeSign{" confDoLikeSign" , false , " Enable like-sign (fake Phi) background calculation" };
358359 } ConfPhiSelection;
359360
360361 // PDG codes for fillMCParticle function
@@ -2229,6 +2230,156 @@ struct FemtoUniverseProducerTask {
22292230 }
22302231 }
22312232
2233+ template <bool isMC, typename TrackType, typename CollisionType>
2234+ void fillLikeSignPhi (CollisionType const & col, TrackType const & tracks)
2235+ {
2236+ std::vector<int > childIDs = {0 , 0 };
2237+ std::vector<int > tmpIDtrack;
2238+
2239+ // Loop over all track combinations using FullIndexPolicy
2240+ for (const auto & [p1, p2] : combinations (soa::CombinationsFullIndexPolicy (tracks, tracks))) {
2241+
2242+ // 1. Check minimal track quality cuts for both tracks
2243+ if (!trackCuts.isSelectedMinimal (p1) || !trackCuts.isSelectedMinimal (p2)) {
2244+ continue ;
2245+ }
2246+
2247+ if (p1.sign () != p2.sign ()) { // K+K- pairs are already handled in fillPhi, so we skip them here, only K+K+ and K-K-
2248+ continue ;
2249+ }
2250+
2251+ // avoid double counting and self-pairing when using FullIndexPolicy
2252+ if (p1.globalIndex () >= p2.globalIndex ()) {
2253+ continue ;
2254+ }
2255+
2256+ // cuts for daughters
2257+ if (ConfPhiSelection.confPhiDoLFPID4Kaons ) {
2258+ if (!p1.isGlobalTrackWoDCA () || !p2.isGlobalTrackWoDCA () || !p1.isPVContributor () || !p2.isPVContributor ()) {
2259+ continue ;
2260+ }
2261+ if (!(isKaonNSigmaLF (p1.pt (), trackCuts.getNsigmaTPC (p1, o2::track::PID ::Kaon), trackCuts.getNsigmaTOF (p1, o2::track::PID ::Kaon), p1.hasTOF ()))) {
2262+ continue ;
2263+ }
2264+ if (!(isKaonNSigmaLF (p2.pt (), trackCuts.getNsigmaTPC (p2, o2::track::PID ::Kaon), trackCuts.getNsigmaTOF (p2, o2::track::PID ::Kaon), p2.hasTOF ()))) {
2265+ continue ;
2266+ }
2267+ } else {
2268+ if (!(isKaonNSigma (p1.pt (), trackCuts.getNsigmaTPC (p1, o2::track::PID ::Kaon), trackCuts.getNsigmaTOF (p1, o2::track::PID ::Kaon)))) {
2269+ continue ;
2270+ }
2271+ if (!(isKaonNSigma (p2.pt (), trackCuts.getNsigmaTPC (p2, o2::track::PID ::Kaon), trackCuts.getNsigmaTOF (p2, o2::track::PID ::Kaon)))) {
2272+ continue ;
2273+ }
2274+ }
2275+
2276+ // Reject protons and pions contamination
2277+ if (isKaonRejected (p1.p (), trackCuts.getNsigmaTPC (p1, o2::track::PID ::Proton), trackCuts.getNsigmaTOF (p1, o2::track::PID ::Proton), trackCuts.getNsigmaTPC (p1, o2::track::PID ::Pion), trackCuts.getNsigmaTOF (p1, o2::track::PID ::Pion))) {
2278+ continue ;
2279+ }
2280+ if (isKaonRejected (p2.p (), trackCuts.getNsigmaTPC (p2, o2::track::PID ::Proton), trackCuts.getNsigmaTOF (p2, o2::track::PID ::Proton), trackCuts.getNsigmaTPC (p2, o2::track::PID ::Pion), trackCuts.getNsigmaTOF (p2, o2::track::PID ::Pion))) {
2281+ continue ;
2282+ }
2283+
2284+ const auto mMassOne = o2::constants::physics::MassKPlus;
2285+ const auto mMassTwo = o2::constants::physics::MassKPlus;
2286+
2287+ ROOT ::Math::LorentzVector<ROOT ::Math::PtEtaPhiM4D<double >> part1Vec (p1.pt (), p1.eta (), p1.phi (), mMassOne );
2288+ ROOT ::Math::LorentzVector<ROOT ::Math::PtEtaPhiM4D<double >> part2Vec (p2.pt (), p2.eta (), p2.phi (), mMassTwo );
2289+
2290+ ROOT ::Math::LorentzVector<ROOT ::Math::PtEtaPhiM4D<double >> sumVec (part1Vec);
2291+ sumVec += part2Vec;
2292+
2293+ // kinematic cuts for phi candidate
2294+ float phiEta = sumVec.Eta ();
2295+ if (std::abs (phiEta) > ConfPhiSelection.confPhiEtaHighLimit .value ) {
2296+ continue ;
2297+ }
2298+
2299+ float phiPt = sumVec.Pt ();
2300+ if ((phiPt < ConfPhiSelection.confPhiPtLowLimit .value ) || (phiPt > ConfPhiSelection.confPhiPtHighLimit .value )) {
2301+ continue ;
2302+ }
2303+
2304+ float phiPhi = RecoDecay::constrainAngle (sumVec.Phi (), 0 );
2305+ float phiM = sumVec.M ();
2306+
2307+ if ((phiM < ConfPhiSelection.confPhiInvMassLowLimit ) || (phiM > ConfPhiSelection.confPhiInvMassUpLimit )) {
2308+ continue ;
2309+ }
2310+
2311+ phiCuts.fillQA <aod::femtouniverseparticle::ParticleType::kPhi , aod::femtouniverseparticle::ParticleType::kPhiChild >(col, p1, p1, p2, 321 , 321 );
2312+
2313+ int postrackID = p1.globalIndex ();
2314+ int rowInPrimaryTrackTablePos = -1 ;
2315+ rowInPrimaryTrackTablePos = getRowDaughters (postrackID, tmpIDtrack);
2316+ childIDs[0 ] = rowInPrimaryTrackTablePos;
2317+ childIDs[1 ] = 0 ;
2318+
2319+ // first daughter track
2320+ outputParts (outputCollision.lastIndex (), p1.pt (),
2321+ p1.eta (), p1.phi (),
2322+ aod::femtouniverseparticle::ParticleType::kPhiChild ,
2323+ -999 ,
2324+ -999 ,
2325+ p1.dcaXY (),
2326+ childIDs,
2327+ 0 ,
2328+ p1.sign ());
2329+ const int rowOfPosTrack = outputParts.lastIndex ();
2330+ if constexpr (isMC) {
2331+ fillMCParticle (p1, o2::aod::femtouniverseparticle::ParticleType::kPhiChild );
2332+ }
2333+
2334+ int negtrackID = p2.globalIndex ();
2335+ int rowInPrimaryTrackTableNeg = -1 ;
2336+ rowInPrimaryTrackTableNeg = getRowDaughters (negtrackID, tmpIDtrack);
2337+ childIDs[0 ] = 0 ;
2338+ childIDs[1 ] = rowInPrimaryTrackTableNeg;
2339+
2340+ // second daughter track
2341+ outputParts (outputCollision.lastIndex (),
2342+ p2.pt (),
2343+ p2.eta (),
2344+ p2.phi (),
2345+ aod::femtouniverseparticle::ParticleType::kPhiChild ,
2346+ -999 ,
2347+ -999 ,
2348+ p2.dcaXY (),
2349+ childIDs,
2350+ 0 ,
2351+ p2.sign ());
2352+ const int rowOfNegTrack = outputParts.lastIndex ();
2353+ if constexpr (isMC) {
2354+ fillMCParticle (p2, o2::aod::femtouniverseparticle::ParticleType::kPhiChild );
2355+ }
2356+
2357+ std::vector<int > indexChildID = {rowOfPosTrack, rowOfNegTrack};
2358+
2359+ // Define like-sign flag: +1 for (K+K+), -1 for (K-K-)
2360+ int likeSignFlag = p1.sign ();
2361+
2362+ // Save the Fake Phi candidate from K+K+ or K-K- combination
2363+ outputParts (outputCollision.lastIndex (),
2364+ phiPt,
2365+ phiEta,
2366+ phiPhi,
2367+ aod::femtouniverseparticle::ParticleType::kPhi ,
2368+ likeSignFlag, // Flag: +1 for K+K+, -1 for K-K-, before it was empty slot for cutcontainer
2369+ 0 ,
2370+ phiM, // Invariant mass of the fake Phi candidate
2371+ indexChildID,
2372+ phiM,
2373+ 0 );
2374+
2375+ if (confIsDebug) {
2376+ fillDebugParticle<true , false , false >(p1); // QA for first daughter
2377+ fillDebugParticle<true , false , false >(p2); // QA for second daughter
2378+ fillDebugParticle<false , true , false >(p1); // QA for fake Phi mother
2379+ }
2380+ }
2381+ }
2382+
22322383 template <typename TrackType, bool transientLabels = false , bool resolveDaughs = false >
22332384 void fillParticles (TrackType const & tracks, std::optional<std::reference_wrapper<const std::set<int >>> recoMcIds = std::nullopt )
22342385 {
@@ -2452,6 +2603,9 @@ struct FemtoUniverseProducerTask {
24522603 }
24532604 if (ConfGeneral.confIsActivatePhi ) {
24542605 fillPhi<isMC>(col, tracks);
2606+ if (ConfPhiSelection.confDoLikeSign ) {
2607+ fillLikeSignPhi<isMC>(col, tracks);
2608+ }
24552609 }
24562610 }
24572611 return colcheck;
@@ -2628,6 +2782,9 @@ struct FemtoUniverseProducerTask {
26282782 if (colcheck) {
26292783 fillTracks<false >(tracks);
26302784 fillPhi<false >(col, tracks);
2785+ if (ConfPhiSelection.confDoLikeSign ) {
2786+ fillLikeSignPhi<false >(col, tracks);
2787+ }
26312788 }
26322789 }
26332790 PROCESS_SWITCH (FemtoUniverseProducerTask, processTrackPhiData,
0 commit comments