Skip to content

Commit 00bcf18

Browse files
[PWGCF] Adding V0s and Phi cases in filter2Prong processing
1 parent e4dd716 commit 00bcf18

File tree

2 files changed

+275
-23
lines changed

2 files changed

+275
-23
lines changed

PWGCF/DataModel/CorrelationsDerived.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ enum ParticleDecay {
123123
D0barToKPi,
124124
JPsiToEE,
125125
JPsiToMuMu,
126-
Generic2Prong,
126+
PhiToKK,
127+
K0stoPiPi,
128+
LambdatoPPi,
129+
AntiLambdatoPiP
127130
};
128131
} // namespace cf2prongtrack
129132
DECLARE_SOA_TABLE(CF2ProngTracks, "AOD", "CF2PRONGTRACK", //! Reduced track table

PWGCF/TableProducer/filter2Prong.cxx

Lines changed: 271 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include "PWGHF/DataModel/CandidateReconstructionTables.h"
1717
#include "PWGHF/DataModel/CandidateSelectionTables.h"
1818

19+
#include "Common/DataModel/PIDResponseITS.h"
20+
1921
#include "Framework/ASoAHelpers.h"
2022
#include "Framework/AnalysisDataModel.h"
2123
#include "Framework/AnalysisTask.h"
@@ -49,6 +51,41 @@ struct Filter2Prong {
4951
O2_DEFINE_CONFIGURABLE(cfgImMaxInvMass, float, 1.07f, "Maximum invariant mass (GeV)")
5052
O2_DEFINE_CONFIGURABLE(cfgImSigmaFormula, std::string, "(z < 0.5 && x < 3.0) || (z >= 0.5 && x < 2.5 && y < 3.0)", "pT dependent daughter track sigma pass condition (x = TPC sigma, y = TOF sigma, z = pT)")
5153

54+
O2_DEFINE_CONFIGURABLE(cfgDoPhi, bool, false, "Store phi information")
55+
O2_DEFINE_CONFIGURABLE(cfgDoV0, bool, true, "Store V0s candidates")
56+
O2_DEFINE_CONFIGURABLE(tpcNClsCrossedRowsTrackMin, float, 70, "Minimum number of crossed rows in TPC")
57+
O2_DEFINE_CONFIGURABLE(etaTrackMax, float, 0.8, "Maximum pseudorapidity")
58+
O2_DEFINE_CONFIGURABLE(ptTrackMin, float, 0.1, "Minimum transverse momentum")
59+
O2_DEFINE_CONFIGURABLE(cMinV0DCAPr, float, 0.1, "Min V0 proton DCA")
60+
O2_DEFINE_CONFIGURABLE(cMinV0DCAPi, float, 0.1, "Min V0 pion DCA")
61+
O2_DEFINE_CONFIGURABLE(ITSPIDSelection, bool, true, "PID ITS")
62+
O2_DEFINE_CONFIGURABLE(ITSPIDPthreshold, float, 1.0, "Momentum threshold for ITS PID (GeV/c) (only used if ITSPIDSelection is true)")
63+
O2_DEFINE_CONFIGURABLE(ITSPIDNsigma, float, 3.0, "PID nsigma for ITS")
64+
O2_DEFINE_CONFIGURABLE(ConfDaughPIDCuts, float, 4.0, "PID nsigma for V0s")
65+
O2_DEFINE_CONFIGURABLE(massK0Min, float, 0.4, "Minimum mass for K0")
66+
O2_DEFINE_CONFIGURABLE(massK0Max, float, 0.6, "Maximum mass for K0")
67+
O2_DEFINE_CONFIGURABLE(massLambdaMin, float, 1.0, "Minimum mass for lambda")
68+
O2_DEFINE_CONFIGURABLE(massLambdaMax, float, 1.3, "Maximum mass for lambda")
69+
O2_DEFINE_CONFIGURABLE(massOmegaMin, float, 1.5, "Minimum mass for omega")
70+
O2_DEFINE_CONFIGURABLE(massOmegaMax, float, 1.8, "Maximum mass for omega")
71+
O2_DEFINE_CONFIGURABLE(interactionRateMin, float, -1, "Minimum interaction rate (kHz)")
72+
O2_DEFINE_CONFIGURABLE(interactionRateMax, float, 1.e20, "Maximum interaction rate (kHz)")
73+
O2_DEFINE_CONFIGURABLE(radiusMax, float, 2.3, "Maximum decay radius (cm)")
74+
O2_DEFINE_CONFIGURABLE(radiusMin, float, 0.0, "Minimum decay radius (cm)")
75+
O2_DEFINE_CONFIGURABLE(cosPaMin, float, 0.98, "Minimum cosine of pointing angle")
76+
O2_DEFINE_CONFIGURABLE(dcaV0DaughtersMax, float, 0.2, "Maximum DCA among the V0 daughters (cm)")
77+
O2_DEFINE_CONFIGURABLE(dcaV0ToPvMax, float, 0.2, "Maximum DCA of the V0 from the primary vertex (cm)")
78+
O2_DEFINE_CONFIGURABLE(cosPaV0Min, float, 0.95, "Minimum cosine of pointing angle for V0 stemming from cascade decays")
79+
O2_DEFINE_CONFIGURABLE(qtArmenterosMinForK0, float, 0.12, "Minimum Armenteros' qt for K0")
80+
O2_DEFINE_CONFIGURABLE(qtArmenterosMaxForLambda, float, 0.12, "Minimum Armenteros' qt for (anti)Lambda")
81+
O2_DEFINE_CONFIGURABLE(ConfV0Rap, float, 0.5, "Store rapidity of v0")
82+
O2_DEFINE_CONFIGURABLE(cMaxLambdaLifeTime, float, 30, "Store Lambda lifetime")
83+
O2_DEFINE_CONFIGURABLE(cMaxK0sLifeTime, float, 30, "Store K0s lifetime")
84+
O2_DEFINE_CONFIGURABLE(isDeepAngle, bool, true, "flag for applying deep angle")
85+
O2_DEFINE_CONFIGURABLE(cfgDeepAngle, float, 0.04, "deep angle cut")
86+
O2_DEFINE_CONFIGURABLE(removefaketrack, bool, true, "flag to remove fake kaon")
87+
O2_DEFINE_CONFIGURABLE(ConfFakeKaonCut, float, 0.1, "Cut based on track from momentum difference")
88+
5289
HfHelper hfHelper;
5390
Produces<aod::CF2ProngTracks> output2ProngTracks;
5491
Produces<aod::CF2ProngTrackmls> output2ProngTrackmls;
@@ -166,37 +203,249 @@ struct Filter2Prong {
166203
}
167204
PROCESS_SWITCH(Filter2Prong, processMC, "Process MC 2-prong daughters", false);
168205

169-
// Generic 2-prong invariant mass method candidate finder. Only works for non-identical daughters of opposite charge for now.
206+
template <typename T1, typename T2>
207+
bool selectionPair(const T1& candidate1, const T2& candidate2)
208+
{
209+
double pt1, pt2, pz1, pz2, p1, p2, angle;
210+
pt1 = candidate1.pt();
211+
pt2 = candidate2.pt();
212+
pz1 = candidate1.pz();
213+
pz2 = candidate2.pz();
214+
p1 = candidate1.p();
215+
p2 = candidate2.p();
216+
angle = TMath::ACos((pt1 * pt2 + pz1 * pz2) / (p1 * p2));
217+
if (isDeepAngle && angle < cfgDeepAngle) {
218+
return false;
219+
}
220+
return true;
221+
}
222+
223+
template <typename T>
224+
bool isFakeKaon(T const& track)
225+
{
226+
const auto pglobal = track.p();
227+
const auto ptpc = track.tpcInnerParam();
228+
if (TMath::Abs(pglobal - ptpc) > ConfFakeKaonCut) {
229+
return true;
230+
}
231+
return false;
232+
}
233+
234+
template <typename Collision, typename V0Cand>
235+
bool isSelectedV0AsK0s(Collision const& collision, const V0Cand& v0)
236+
{
237+
const auto& posTrack = v0.template posTrack_as<PIDTrack>();
238+
const auto& negTrack = v0.template negTrack_as<PIDTrack>();
239+
240+
float CtauK0s = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0;
241+
242+
if (v0.mK0Short() < massK0Min || v0.mK0Short() > massK0Max) {
243+
return false;
244+
}
245+
if (v0.qtarm() < qtArmenterosMinForK0) {
246+
return false;
247+
}
248+
if (v0.v0radius() > radiusMax) {
249+
return false;
250+
}
251+
if (v0.v0radius() < radiusMin) {
252+
return false;
253+
}
254+
if (v0.v0cosPA() < cosPaMin) {
255+
return false;
256+
}
257+
if (v0.dcaV0daughters() > dcaV0DaughtersMax) {
258+
return false;
259+
}
260+
if (v0.dcav0topv() > dcaV0ToPvMax) {
261+
return false;
262+
}
263+
if (std::abs(CtauK0s) > cMaxK0sLifeTime) {
264+
return false;
265+
}
266+
if (std::abs(v0.yK0Short()) > ConfV0Rap) {
267+
return false;
268+
}
269+
if (((std::abs(posTrack.tpcNSigmaPi()) > ConfDaughPIDCuts) || (std::abs(negTrack.tpcNSigmaPi()) > ConfDaughPIDCuts))) {
270+
return false;
271+
}
272+
if ((TMath::Abs(v0.dcapostopv()) < cMinV0DCAPi || TMath::Abs(v0.dcanegtopv()) < cMinV0DCAPi)) {
273+
return false;
274+
}
275+
return true;
276+
}
277+
278+
template <typename Collision, typename V0Cand>
279+
bool isSelectedV0AsLambda(Collision const& collision, const V0Cand& v0, int pid /*0: lambda, 1: antilambda*/)
280+
{
281+
const auto& posTrack = v0.template posTrack_as<PIDTrack>();
282+
const auto& negTrack = v0.template negTrack_as<PIDTrack>();
283+
284+
float CtauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda;
285+
286+
if ((v0.mLambda() < massLambdaMin || v0.mLambda() > massLambdaMax) &&
287+
(v0.mAntiLambda() < massLambdaMin || v0.mAntiLambda() > massLambdaMax)) {
288+
return false;
289+
}
290+
if (v0.qtarm() > qtArmenterosMaxForLambda) {
291+
return false;
292+
}
293+
if (v0.v0radius() > radiusMax) {
294+
return false;
295+
}
296+
if (v0.v0radius() < radiusMin) {
297+
return false;
298+
}
299+
if (v0.v0cosPA() < cosPaMin) {
300+
return false;
301+
}
302+
if (v0.dcaV0daughters() > dcaV0DaughtersMax) {
303+
return false;
304+
}
305+
if (v0.dcav0topv() > dcaV0ToPvMax) {
306+
return false;
307+
}
308+
if (pid == 0 && (TMath::Abs(v0.dcapostopv()) < cMinV0DCAPr || TMath::Abs(v0.dcanegtopv()) < cMinV0DCAPi)) {
309+
return false;
310+
}
311+
if (pid == 1 && (TMath::Abs(v0.dcapostopv()) < cMinV0DCAPi || TMath::Abs(v0.dcanegtopv()) < cMinV0DCAPr)) {
312+
return false;
313+
}
314+
if (pid == 0 && ((std::abs(posTrack.tpcNSigmaPr()) > ConfDaughPIDCuts) || (std::abs(negTrack.tpcNSigmaPi()) > ConfDaughPIDCuts))) {
315+
return false;
316+
}
317+
if (pid == 1 && ((std::abs(posTrack.tpcNSigmaPi()) > ConfDaughPIDCuts) || (std::abs(negTrack.tpcNSigmaPr()) > ConfDaughPIDCuts))) {
318+
return false;
319+
}
320+
if (std::abs(CtauLambda) > cMaxLambdaLifeTime) {
321+
return false;
322+
}
323+
if (std::abs(v0.yLambda()) > ConfV0Rap) {
324+
return false;
325+
}
326+
return true;
327+
}
328+
329+
template <typename T1>
330+
bool isV0TrackSelected(const T1& v0)
331+
{
332+
const auto& posTrack = v0.template posTrack_as<PIDTrack>();
333+
const auto& negTrack = v0.template negTrack_as<PIDTrack>();
334+
335+
if (!posTrack.hasTPC() || !negTrack.hasTPC()) {
336+
return false;
337+
}
338+
if (posTrack.tpcNClsCrossedRows() < tpcNClsCrossedRowsTrackMin || negTrack.tpcNClsCrossedRows() < tpcNClsCrossedRowsTrackMin) {
339+
return false;
340+
}
341+
if (posTrack.tpcCrossedRowsOverFindableCls() < 0.8 || negTrack.tpcCrossedRowsOverFindableCls() < 0.8) {
342+
return false;
343+
}
344+
if (std::abs(v0.positiveeta()) > etaTrackMax || std::abs(v0.negativeeta()) > etaTrackMax) {
345+
return false;
346+
}
347+
if (v0.positivept() < ptTrackMin || v0.negativept() < ptTrackMin) {
348+
return false;
349+
}
350+
return true;
351+
}
352+
353+
// Processing data for invariant mass analysis for 2-prong tracks, including V0s (K0s, Lambdas, Anti-Lambdas) and Phi mesons
170354
using PIDTrack = soa::Join<aod::Tracks, aod::TracksExtra, aod::TrackSelection, aod::pidTPCPi, aod::pidTPCKa, aod::pidTPCPr, aod::pidTOFPi, aod::pidTOFKa, aod::pidTOFPr>;
171-
void processDataInvMass(aod::Collisions::iterator const&, aod::BCsWithTimestamps const&, aod::CFCollRefs const& cfcollisions, aod::CFTrackRefs const& cftracks, PIDTrack const& tracks)
355+
void processDataInvMass(aod::Collisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::CFCollRefs const& cfcollisions, aod::CFTrackRefs const& cftracks, PIDTrack const& tracks, aod::V0Datas const& V0s)
172356
{
173357
if (cfcollisions.size() <= 0 || cftracks.size() <= 0)
174358
return; // rejected collision
175-
for (const auto& cftrack1 : cftracks) {
176-
const auto& p1 = tracks.iteratorAt(cftrack1.trackId() - tracks.begin().globalIndex());
177-
if (p1.sign() != 1)
178-
continue;
179-
if (sigmaFormula->Eval(o2::aod::pidutils::tpcNSigma(cfgImPart1PID, p1), o2::aod::pidutils::tofNSigma(cfgImPart1PID, p1)) <= 0.0f)
180-
continue;
181-
for (const auto& cftrack2 : cftracks) {
182-
if (cftrack2.globalIndex() == cftrack1.globalIndex())
359+
360+
o2::aod::ITSResponse itsResponse;
361+
362+
if (cfgDoPhi) { // Process Phi mesons
363+
for (const auto& cftrack1 : cftracks) { // Loop over first track
364+
const auto& p1 = tracks.iteratorAt(cftrack1.trackId() - tracks.begin().globalIndex());
365+
366+
if (p1.sign() != 1) // Only consider positive tracks
183367
continue;
184-
const auto& p2 = tracks.iteratorAt(cftrack2.trackId() - tracks.begin().globalIndex());
185-
if (p2.sign() != -1)
368+
if (sigmaFormula->Eval(o2::aod::pidutils::tpcNSigma(cfgImPart1PID, p1), o2::aod::pidutils::tofNSigma(cfgImPart1PID, p1)) <= 0.0f) // Check if the track passes PID condition
186369
continue;
187-
if (sigmaFormula->Eval(o2::aod::pidutils::tpcNSigma(cfgImPart2PID, p2), o2::aod::pidutils::tofNSigma(cfgImPart2PID, p2)) <= 0.0f)
370+
if (ITSPIDSelection && p1.p() < ITSPIDPthreshold.value && !(itsResponse.nSigmaITS<o2::track::PID::Kaon>(p1) > -ITSPIDNsigma.value && itsResponse.nSigmaITS<o2::track::PID::Kaon>(p1) < ITSPIDNsigma.value)) { // Check ITS PID condition
371+
continue;
372+
}
373+
if (removefaketrack && isFakeKaon(p1)) { // Check if the track is a fake kaon
188374
continue;
189-
ROOT::Math::PtEtaPhiMVector vec1(p1.pt(), p1.eta(), p1.phi(), cfgImPart1Mass);
190-
ROOT::Math::PtEtaPhiMVector vec2(p2.pt(), p2.eta(), p2.phi(), cfgImPart2Mass);
191-
ROOT::Math::PtEtaPhiMVector s = vec1 + vec2;
192-
if (s.pt() < cfgImCutPt || s.M() < cfgImMinInvMass || s.M() > cfgImMaxInvMass)
375+
}
376+
377+
for (const auto& cftrack2 : cftracks) { // Loop over second track
378+
if (cftrack2.globalIndex() == cftrack1.globalIndex()) // Skip if it's the same track as the first one
379+
continue;
380+
381+
const auto& p2 = tracks.iteratorAt(cftrack2.trackId() - tracks.begin().globalIndex());
382+
if (p2.sign() != -1) // Only consider negative tracks
383+
continue;
384+
if (sigmaFormula->Eval(o2::aod::pidutils::tpcNSigma(cfgImPart2PID, p2), o2::aod::pidutils::tofNSigma(cfgImPart2PID, p2)) <= 0.0f) // Check if the track passes PID condition
385+
continue;
386+
if (ITSPIDSelection && p2.p() < ITSPIDPthreshold.value && !(itsResponse.nSigmaITS<o2::track::PID::Kaon>(p2) > -ITSPIDNsigma.value && itsResponse.nSigmaITS<o2::track::PID::Kaon>(p2) < ITSPIDNsigma.value)) { // Check ITS PID condition
387+
continue;
388+
}
389+
if (!selectionPair(p1, p2)) {
390+
continue;
391+
}
392+
if (removefaketrack && isFakeKaon(p2)) {
393+
continue;
394+
}
395+
396+
ROOT::Math::PtEtaPhiMVector vec1(p1.pt(), p1.eta(), p1.phi(), cfgImPart1Mass);
397+
ROOT::Math::PtEtaPhiMVector vec2(p2.pt(), p2.eta(), p2.phi(), cfgImPart2Mass);
398+
ROOT::Math::PtEtaPhiMVector s = vec1 + vec2;
399+
if (s.pt() < cfgImCutPt || s.M() < cfgImMinInvMass || s.M() > cfgImMaxInvMass)
400+
continue;
401+
402+
float phi = RecoDecay::constrainAngle(s.Phi(), 0.0f);
403+
output2ProngTracks(cfcollisions.begin().globalIndex(),
404+
cftrack1.globalIndex(), cftrack2.globalIndex(), s.pt(), s.eta(), phi, s.M(), aod::cf2prongtrack::PhiToKK);
405+
} // end of loop over second track
406+
} // end of loop over first track
407+
} // end of processing Phi mesons
408+
409+
if (cfgDoV0) { // Process V0 candidates (K0s, Lambdas, Anti-Lambdas)
410+
for (const auto& v0 : V0s) { // Loop over V0 candidates
411+
if (!isV0TrackSelected(v0)) { // Quality selection for V0 prongs
193412
continue;
413+
}
194414

195-
float phi = RecoDecay::constrainAngle(s.Phi(), 0.0f);
196-
output2ProngTracks(cfcollisions.begin().globalIndex(),
197-
cftrack1.globalIndex(), cftrack2.globalIndex(), s.pt(), s.eta(), phi, s.M(), aod::cf2prongtrack::Generic2Prong);
198-
}
199-
}
415+
const auto& posTrack = v0.template posTrack_as<PIDTrack>();
416+
const auto& negTrack = v0.template negTrack_as<PIDTrack>();
417+
418+
auto v0Type = 0;
419+
double massV0 = 0.0;
420+
if (isSelectedV0AsK0s(collision, v0)) { // candidate is K0s
421+
SETBIT(v0Type, aod::cf2prongtrack::K0stoPiPi);
422+
423+
output2ProngTracks(cfcollisions.begin().globalIndex(),
424+
posTrack.globalIndex(), negTrack.globalIndex(),
425+
v0.pt(), v0.eta(), v0.phi(), v0.mK0Short(), v0Type);
426+
427+
continue; // candidate is K0s, skip to next V0 candidate
428+
}
429+
430+
bool LambdaTag = isSelectedV0AsLambda(collision, v0, 0);
431+
bool aLambdaTag = isSelectedV0AsLambda(collision, v0, 1);
432+
if (!LambdaTag && !aLambdaTag) { // neither Lambda nor Anti-Lambda
433+
continue;
434+
}
435+
// Note: candidate compatible with Lambda and Anti-Lambda hypothesis are counted twice (once for each hypothesis)
436+
if (LambdaTag) { // candidate is Lambda
437+
SETBIT(v0Type, aod::cf2prongtrack::LambdatoPPi);
438+
massV0 = v0.mLambda();
439+
output2ProngTracks(cfcollisions.begin().globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(),
440+
v0.pt(), v0.eta(), v0.phi(), massV0, v0Type);
441+
} else if (aLambdaTag) { // candidate is Anti-lambda
442+
SETBIT(v0Type, aod::cf2prongtrack::AntiLambdatoPiP);
443+
massV0 = v0.mAntiLambda();
444+
output2ProngTracks(cfcollisions.begin().globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(),
445+
v0.pt(), v0.eta(), v0.phi(), massV0, v0Type);
446+
} // end of Lambda and Anti-Lambda processing
447+
} // end of loop over V0 candidates
448+
} // end of processing V0 candidates
200449
}
201450
PROCESS_SWITCH(Filter2Prong, processDataInvMass, "Process data generic 2-prong candidates with invariant mass method", false);
202451
}; // struct

0 commit comments

Comments
 (0)