@@ -45,6 +45,13 @@ enum LambdaPid { kLambda = 0,
4545#define O2_DEFINE_CONFIGURABLE (NAME, TYPE, DEFAULT, HELP ) Configurable<TYPE> NAME{#NAME, DEFAULT, HELP};
4646
4747struct Filter2Prong {
48+ SliceCache cache;
49+ struct MixEvent {
50+ int collRefGlobalIndex; // same convention as cfcollisions.begin().globalIndex()
51+ int originalCollisionId; // needed to slice CFTrackRefs
52+ };
53+ std::vector<std::vector<MixEvent>> mixingPools;
54+
4855 O2_DEFINE_CONFIGURABLE (cfgVerbosity, int , 0 , " Verbosity level (0 = major, 1 = per collision)" )
4956 O2_DEFINE_CONFIGURABLE (cfgYMax, float , -1 .0f , " Maximum candidate rapidity" )
5057 O2_DEFINE_CONFIGURABLE (cfgImPart1Mass, float , o2::constants::physics::MassKPlus, " Daughter particle 1 mass in GeV" )
@@ -146,12 +153,18 @@ struct Filter2Prong {
146153 O2_DEFINE_CONFIGURABLE (applyTOF, bool , false , " Flag for applying TOF" );
147154 } grpPhi;
148155
156+ O2_DEFINE_CONFIGURABLE (cfgNoMixedEvents, int , 5 , " Number of mixed events per event for mixed phi building" )
157+ ConfigurableAxis axisVertexMix{" axisVertexMix" , {7 , -7 , 7 }, " vertex axis for phi event mixing" };
158+ ConfigurableAxis axisMultiplicityMix{" axisMultiplicityMix" , {VARIABLE_WIDTH, 0 , 5 , 10 , 20 , 30 , 40 , 50 , 100.1 }, " multiplicity axis for phi event mixing" };
159+
149160 HfHelper hfHelper;
150161 Produces<aod::CF2ProngTracks> output2ProngTracks;
151162 Produces<aod::CF2ProngTrackmls> output2ProngTrackmls;
152163
153164 Produces<aod::CF2ProngMcParts> output2ProngMcParts;
154165
166+ Produces<aod::CFMixedPhiTracks> outputMixedPhiTracks;
167+
155168 std::vector<float > mlvecd{};
156169 std::vector<float > mlvecdbar{};
157170
@@ -172,6 +185,7 @@ struct Filter2Prong {
172185
173186 void init (InitContext&)
174187 {
188+
175189 if (doprocessDataInvMass) {
176190 sigmaFormula = std::make_unique<TFormula>(" sigmaFormula" , cfgImSigmaFormula.value .c_str ());
177191 if (static_cast <std::size_t >(sigmaFormula->GetNpar ()) > std::size (sigmaFormulaParamIndex))
@@ -188,6 +202,12 @@ struct Filter2Prong {
188202 }
189203 }
190204 }
205+ int getMixBin (float zvtx, float mult)
206+ {
207+ using BinningTypeDerived = ColumnBinningPolicy<aod::collision::PosZ, aod::cfcollision::Multiplicity>;
208+ BinningTypeDerived configurableBinning{{axisVertexMix, axisMultiplicityMix}, true };
209+ return configurableBinning.getBin ({zvtx, mult});
210+ }
191211
192212 template <class HFCandidatesType >
193213 void processDataT (aod::Collisions::iterator const &, aod::BCsWithTimestamps const &, aod::CFCollRefs const & cfcollisions, aod::CFTrackRefs const & cftracks, HFCandidatesType const & candidates)
@@ -772,6 +792,125 @@ struct Filter2Prong {
772792 }
773793 PROCESS_SWITCH (Filter2Prong, processDataPhiV0, " Process data Phi and V0 candidates with invariant mass method" , false );
774794
795+ using MixCollisions = aod::CFCollisions;
796+ Preslice<aod::CFTrackRefs> perCollisionCFTrackRef = aod::track::collisionId;
797+ void processDataPhiMixed (aod::CFCollisions const & collisions,
798+ aod::CFCollRefs const & cfcollrefs,
799+ aod::CFTrackRefs const & cftracks,
800+ Filter2Prong::PIDTrack const & tracks)
801+ {
802+ const int nMixBins = AxisSpec (axisVertexMix).getNbins () * AxisSpec (axisMultiplicityMix).getNbins ();
803+ if (mixingPools.empty ()) {
804+ mixingPools.resize (nMixBins);
805+ }
806+
807+ using BinningTypeDerived = ColumnBinningPolicy<aod::collision::PosZ, aod::cfcollision::Multiplicity>;
808+ BinningTypeDerived configurableBinning{{axisVertexMix, axisMultiplicityMix}, true };
809+
810+ o2::aod::ITSResponse itsResponse;
811+
812+ for (const auto & collision : collisions) {
813+ // int reducedCollisionId = collision.globalIndex();
814+
815+ int redIdx = collision.globalIndex () - collisions.begin ().globalIndex ();
816+ const auto & collRef = cfcollrefs.iteratorAt (redIdx);
817+ int originalCollisionId = collRef.collisionId ();
818+ int collRefGlobalIndex = collRef.globalIndex ();
819+
820+ float zvtx = collision.posZ ();
821+ float mult = collision.multiplicity ();
822+ int mixBin = configurableBinning.getBin ({zvtx, mult});
823+ if (mixBin < 0 || mixBin >= nMixBins) {
824+ continue ;
825+ }
826+
827+ auto tracksCurrentRefs = cftracks.sliceBy (perCollisionCFTrackRef, originalCollisionId);
828+
829+ for (const auto & prevEvent : mixingPools[mixBin]) {
830+ if (prevEvent.originalCollisionId == originalCollisionId) {
831+ continue ;
832+ }
833+ auto tracksMixedRefs = cftracks.sliceBy (perCollisionCFTrackRef, prevEvent.originalCollisionId );
834+
835+ for (const auto & cftrack1 : tracksCurrentRefs) {
836+ const auto & p1 = tracks.iteratorAt (cftrack1.trackId () - tracks.begin ().globalIndex ());
837+
838+ if (p1.sign () != 1 ) {
839+ continue ;
840+ }
841+ if (!selectionTrack (p1)) {
842+ continue ;
843+ }
844+ if (grpPhi.ITSPIDSelection &&
845+ p1.p () < grpPhi.ITSPIDPthreshold .value &&
846+ !(itsResponse.nSigmaITS <o2::track::PID::Kaon>(p1) > grpPhi.lowITSPIDNsigma .value &&
847+ itsResponse.nSigmaITS <o2::track::PID::Kaon>(p1) < grpPhi.highITSPIDNsigma .value )) {
848+ continue ;
849+ }
850+ if (grpPhi.removefaketrack && isFakeTrack (p1)) {
851+ continue ;
852+ }
853+
854+ for (const auto & cftrack2 : tracksMixedRefs) {
855+ const auto & p2 = tracks.iteratorAt (cftrack2.trackId () - tracks.begin ().globalIndex ());
856+
857+ if (p2.sign () != -1 ) {
858+ continue ;
859+ }
860+ if (!selectionTrack (p2)) {
861+ continue ;
862+ }
863+ if (grpPhi.ITSPIDSelection &&
864+ p2.p () < grpPhi.ITSPIDPthreshold .value &&
865+ !(itsResponse.nSigmaITS <o2::track::PID::Kaon>(p2) > grpPhi.lowITSPIDNsigma .value &&
866+ itsResponse.nSigmaITS <o2::track::PID::Kaon>(p2) < grpPhi.highITSPIDNsigma .value )) {
867+ continue ;
868+ }
869+ if (grpPhi.removefaketrack && isFakeTrack (p2)) {
870+ continue ;
871+ }
872+ if (!selectionPair (p1, p2)) {
873+ continue ;
874+ }
875+
876+ if (selectionPID3 (p1) && selectionPID3 (p2)) {
877+ if (selectionSys (p1, false , false ) && selectionSys (p2, false , false )) {
878+
879+ ROOT::Math::PtEtaPhiMVector vec1 (p1.pt (), p1.eta (), p1.phi (), cfgImPart1Mass);
880+ ROOT::Math::PtEtaPhiMVector vec2 (p2.pt (), p2.eta (), p2.phi (), cfgImPart2Mass);
881+ ROOT::Math::PtEtaPhiMVector s = vec1 + vec2;
882+
883+ if (s.M () < grpPhi.ImMinInvMassPhiMeson || s.M () > grpPhi.ImMaxInvMassPhiMeson ) {
884+ continue ;
885+ }
886+
887+ float phi = RecoDecay::constrainAngle (s.Phi (), 0 .0f );
888+
889+ outputMixedPhiTracks (collRefGlobalIndex,
890+ prevEvent.collRefGlobalIndex ,
891+ cftrack1.globalIndex (),
892+ cftrack2.globalIndex (),
893+ p1.phi (),
894+ p2.phi (),
895+ s.pt (),
896+ s.eta (),
897+ phi,
898+ s.M ());
899+ }
900+ }
901+ }
902+ }
903+ }
904+
905+ mixingPools[mixBin].push_back ({collRefGlobalIndex, originalCollisionId});
906+
907+ if ((int )mixingPools[mixBin].size () > cfgNoMixedEvents) {
908+ mixingPools[mixBin].erase (mixingPools[mixBin].begin ());
909+ }
910+ }
911+ }
912+ PROCESS_SWITCH (Filter2Prong, processDataPhiMixed, " Process mixed-event phi candidates" , false );
913+
775914 // Phi and V0s invariant mass method candidate finder. Only works for non-identical daughters of opposite charge for now.
776915 void processDataV0 (aod::Collisions::iterator const & collision, aod::BCsWithTimestamps const &, aod::CFCollRefs const & cfcollisions, aod::CFTrackRefs const & cftracks, Filter2Prong::PIDTrack const &, aod::V0Datas const & V0s)
777916 {
0 commit comments