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