@@ -64,10 +64,10 @@ namespace o2::aod
6464{
6565namespace full
6666{
67- DECLARE_SOA_COLUMN (M, m, float ); // ! Invariant mass of candidate (GeV/c2)
68- DECLARE_SOA_COLUMN (Pt, pt, float ); // ! Transverse momentum of candidate (GeV/c)
69- DECLARE_SOA_COLUMN (MlScore0, mlScore0, float ); // ! ML score of the first configured index
70- DECLARE_SOA_COLUMN (MlScore1, mlScore1, float ); // ! ML score of the second configured index
67+ DECLARE_SOA_COLUMN (M, m, float ); // ! Invariant mass of candidate (GeV/c2)
68+ DECLARE_SOA_COLUMN (Pt, pt, float ); // ! Transverse momentum of candidate (GeV/c)
69+ DECLARE_SOA_COLUMN (MlScore0, mlScore0, float ); // ! ML score of the first configured index
70+ DECLARE_SOA_COLUMN (MlScore1, mlScore1, float ); // ! ML score of the second configured index
7171DECLARE_SOA_COLUMN (ScalarProd, scalarProd, float ); // ! Scalar product
7272DECLARE_SOA_COLUMN (Cent, cent, float ); // ! Centrality
7373} // namespace full
@@ -125,6 +125,7 @@ struct HfTaskFlowCharmHadrons {
125125 Configurable<float > ptDownSampleMax{" ptDownSampleMax" , 10 ., " Maximum pt for the application of the downsampling factor" };
126126 Configurable<bool > storeResoOccu{" storeResoOccu" , false , " Flag to store Occupancy in resolution ThnSparse" };
127127 Configurable<bool > storeEpCosSin{" storeEpCosSin" , false , " Flag to store cos and sin of EP angle in ThnSparse" };
128+ Configurable<bool > storeCandEta{" storeCandEta" , false , " Flag to store candidates eta" };
128129 Configurable<int > occEstimator{" occEstimator" , 0 , " Occupancy estimation (0: None, 1: ITS, 2: FT0C)" };
129130 Configurable<bool > saveEpResoHisto{" saveEpResoHisto" , false , " Flag to save event plane resolution histogram" };
130131 Configurable<std::string> ccdbUrl{" ccdbUrl" , " http://alice-ccdb.cern.ch" , " url of the ccdb repository" };
@@ -194,6 +195,7 @@ struct HfTaskFlowCharmHadrons {
194195 ConfigurableAxis thnConfigAxisResoFT0cFV0a{" thnConfigAxisResoFT0cFV0a" , {160 , -8 , 8 }, " " };
195196 ConfigurableAxis thnConfigAxisResoFT0cTPCtot{" thnConfigAxisResoFT0cTPCtot" , {160 , -8 , 8 }, " " };
196197 ConfigurableAxis thnConfigAxisResoFV0aTPCtot{" thnConfigAxisResoFV0aTPCtot" , {160 , -8 , 8 }, " " };
198+ ConfigurableAxis thnConfigAxisCandidateEta{" thnConfigAxisCandidateEta" , {100 , -5 , 5 }, " " };
197199
198200 HistogramRegistry registry{" registry" , {}};
199201
@@ -214,6 +216,7 @@ struct HfTaskFlowCharmHadrons {
214216 const AxisSpec thnAxisMlTwo{thnConfigAxisMlTwo, " FD score" };
215217 const AxisSpec thnAxisOccupancyITS{thnConfigAxisOccupancyITS, " OccupancyITS" };
216218 const AxisSpec thnAxisOccupancyFT0C{thnConfigAxisOccupancyFT0C, " OccupancyFT0C" };
219+ const AxisSpec thnAxisCandEta{thnConfigAxisCandidateEta, " #eta" };
217220 const AxisSpec thnAxisNoSameBunchPileup{thnConfigAxisNoSameBunchPileup, " NoSameBunchPileup" };
218221 const AxisSpec thnAxisOccupancy{thnConfigAxisOccupancy, " Occupancy" };
219222 const AxisSpec thnAxisNoCollInTimeRangeNarrow{thnConfigAxisNoCollInTimeRangeNarrow, " NoCollInTimeRangeNarrow" };
@@ -231,6 +234,9 @@ struct HfTaskFlowCharmHadrons {
231234 if (storeMl) {
232235 axes.insert (axes.end (), {thnAxisMlOne, thnAxisMlTwo});
233236 }
237+ if (storeCandEta) {
238+ axes.insert (axes.end (), {thnAxisCandEta});
239+ }
234240 if (occEstimator != 0 ) {
235241 if (occEstimator == 1 ) {
236242 axes.insert (axes.end (), {thnAxisOccupancyITS, thnAxisNoSameBunchPileup, thnAxisOccupancy,
@@ -317,30 +323,36 @@ struct HfTaskFlowCharmHadrons {
317323 void getQvecDtracks (T1 const & cand,
318324 std::vector<float >& tracksQx,
319325 std::vector<float >& tracksQy,
320- const float ampl)
326+ const float amplQVec,
327+ QvecEstimator qvecDetector)
321328 {
322- // TODO: add possibility to consider different weights for the tracks, at the moment only pT is considered;
323- float const pXTrack0 = cand.pxProng0 ();
324- float const pYTrack0 = cand.pyProng0 ();
325- float const pTTrack0 = cand.ptProng0 ();
326- float const phiTrack0 = std::atan2 (pYTrack0, pXTrack0);
327- float const pXTrack1 = cand.pxProng1 ();
328- float const pYTrack1 = cand.pyProng1 ();
329- float const pTTrack1 = cand.ptProng1 ();
330- float const phiTrack1 = std::atan2 (pYTrack1, pXTrack1);
331-
332- tracksQx.push_back (std::cos (harmonic * phiTrack0) * pTTrack0 / ampl);
333- tracksQy.push_back (std::sin (harmonic * phiTrack0) * pTTrack0 / ampl);
334- tracksQx.push_back (std::cos (harmonic * phiTrack1) * pTTrack1 / ampl);
335- tracksQy.push_back (std::sin (harmonic * phiTrack1) * pTTrack1 / ampl);
329+ auto addProngIfInSubevent = [&](float px, float py, float pz) {
330+ const std::array<float , 3 > pVec{px, py, pz};
331+ const float eta = RecoDecay::eta (pVec);
332+
333+ // only subtract daughters that actually contributed to THIS subevent Q
334+ if (qvecDetector == QvecEstimator::TPCPos && eta <= 0 .f ) {
335+ return ;
336+ }
337+ if (qvecDetector == QvecEstimator::TPCNeg && eta >= 0 .f ) {
338+ return ;
339+ }
340+ // for TPCTot: no early return, all prongs contribute
341+
342+ const float pt = RecoDecay::pt (pVec); // or std::hypot(px, py);
343+ const float phi = std::atan2 (py, px);
344+
345+ // xQVec,yQVec are normalized with amplQVec, so the daughters' contribution must use the SAME normalization.
346+ tracksQx.push_back (std::cos (harmonic * phi) * pt / amplQVec);
347+ tracksQy.push_back (std::sin (harmonic * phi) * pt / amplQVec);
348+ };
336349
350+ addProngIfInSubevent (cand.pxProng0 (), cand.pyProng0 (), cand.pzProng0 ());
351+ addProngIfInSubevent (cand.pxProng1 (), cand.pyProng1 (), cand.pzProng1 ());
352+
353+ // 3-prong channels
337354 if constexpr (Channel != DecayChannel::D0ToPiK && Channel != DecayChannel::D0ToKPi) {
338- float const pXTrack2 = cand.pxProng2 ();
339- float const pYTrack2 = cand.pyProng2 ();
340- float const pTTrack2 = cand.ptProng2 ();
341- float const phiTrack2 = std::atan2 (pYTrack2, pXTrack2);
342- tracksQx.push_back (std::cos (harmonic * phiTrack2) * pTTrack2 / ampl);
343- tracksQy.push_back (std::sin (harmonic * phiTrack2) * pTTrack2 / ampl);
355+ addProngIfInSubevent (cand.pxProng2 (), cand.pyProng2 (), cand.pzProng2 ());
344356 }
345357 }
346358
@@ -353,34 +365,31 @@ struct HfTaskFlowCharmHadrons {
353365 void getQvecXic0Tracks (const T1& cand,
354366 std::vector<float >& tracksQx,
355367 std::vector<float >& tracksQy,
356- float ampl)
368+ float amplQVec,
369+ QvecEstimator qvecDetector)
357370 {
358- // add possibility to consider different weights for the tracks, at the moment only pT is considered;
359- float const pXTrack0 = cand.pxPosV0Dau ();
360- float const pYTrack0 = cand.pyPosV0Dau ();
361- float const pTTrack0 = std::hypot (pXTrack0, pYTrack0);
362- float const phiTrack0 = std::atan2 (pXTrack0, pYTrack0);
363- float const pXTrack1 = cand.pxNegV0Dau ();
364- float const pYTrack1 = cand.pyNegV0Dau ();
365- float const pTTrack1 = std::hypot (pXTrack1, pYTrack1);
366- float const phiTrack1 = std::atan2 (pXTrack1, pYTrack1);
367- float const pYTrack2 = cand.pxBachFromCasc ();
368- float const pXTrack2 = cand.pyBachFromCasc ();
369- float const pTTrack2 = std::hypot (pXTrack2, pYTrack2);
370- float const phiTrack2 = std::atan2 (pXTrack2, pYTrack2);
371- float const pXTrack3 = cand.pxBachFromCharmBaryon ();
372- float const pYTrack3 = cand.pyBachFromCharmBaryon ();
373- float const pTTrack3 = std::hypot (pXTrack3, pYTrack3);
374- float const phiTrack3 = std::atan2 (pXTrack3, pYTrack3);
375-
376- tracksQx.push_back (std::cos (harmonic * phiTrack0) * pTTrack0 / ampl);
377- tracksQy.push_back (std::sin (harmonic * phiTrack0) * pTTrack0 / ampl);
378- tracksQx.push_back (std::cos (harmonic * phiTrack1) * pTTrack1 / ampl);
379- tracksQy.push_back (std::sin (harmonic * phiTrack1) * pTTrack1 / ampl);
380- tracksQx.push_back (std::cos (harmonic * phiTrack2) * pTTrack2 / ampl);
381- tracksQy.push_back (std::sin (harmonic * phiTrack2) * pTTrack2 / ampl);
382- tracksQx.push_back (std::cos (harmonic * phiTrack3) * pTTrack3 / ampl);
383- tracksQy.push_back (std::sin (harmonic * phiTrack3) * pTTrack3 / ampl);
371+ auto addProngIfInSubevent = [&](float px, float py, float pz) {
372+ const std::array<float , 3 > pVec{px, py, pz};
373+ const float eta = RecoDecay::eta (pVec);
374+
375+ if (qvecDetector == QvecEstimator::TPCPos && eta <= 0 .f ) {
376+ return ;
377+ }
378+ if (qvecDetector == QvecEstimator::TPCNeg && eta >= 0 .f ) {
379+ return ;
380+ }
381+
382+ const float pt = RecoDecay::pt (pVec);
383+ const float phi = std::atan2 (py, px);
384+
385+ tracksQx.push_back (std::cos (harmonic * phi) * pt / amplQVec);
386+ tracksQy.push_back (std::sin (harmonic * phi) * pt / amplQVec);
387+ };
388+
389+ addProngIfInSubevent (cand.pxPosV0Dau (), cand.pyPosV0Dau (), cand.pzPosV0Dau ());
390+ addProngIfInSubevent (cand.pxNegV0Dau (), cand.pyNegV0Dau (), cand.pzNegV0Dau ());
391+ addProngIfInSubevent (cand.pxBachFromCasc (), cand.pyBachFromCasc (), cand.pzBachFromCasc ());
392+ addProngIfInSubevent (cand.pxBachFromCharmBaryon (), cand.pyBachFromCharmBaryon (), cand.pzBachFromCharmBaryon ());
384393 }
385394 // / Compute the delta psi in the range [0, pi/harmonic]
386395 // / \param psi1 is the first angle
@@ -408,6 +417,7 @@ struct HfTaskFlowCharmHadrons {
408417 // / Fill THnSparse
409418 // / \param mass is the invariant mass of the candidate
410419 // / \param pt is the transverse momentum of the candidate
420+ // / \param eta is the pseudorapidityof the candidate
411421 // / \param cent is the centrality of the collision
412422 // / \param cosNPhi is the cosine of the n*phi angle
413423 // / \param sinNPhi is the sine of the n*phi angle
@@ -418,6 +428,7 @@ struct HfTaskFlowCharmHadrons {
418428 // / \param hfevselflag flag of the collision associated to utilsEvSelHf.h
419429 void fillThn (const float mass,
420430 const float pt,
431+ const float eta,
421432 const float cent,
422433 const float cosNPhi,
423434 const float sinNPhi,
@@ -427,40 +438,50 @@ struct HfTaskFlowCharmHadrons {
427438 const float occupancy,
428439 const o2::hf_evsel::HfCollisionRejectionMask hfevselflag)
429440 {
441+ auto hSparse = registry.get <THnSparse>(HIST (" hSparseFlowCharm" ));
442+ const int ndim = hSparse->GetNdimensions ();
443+
444+ std::vector<double > values;
445+ values.reserve (ndim);
446+
447+ values.push_back (mass);
448+ values.push_back (pt);
449+ values.push_back (cent);
450+ values.push_back (sp);
451+
452+ if (storeEP) {
453+ values.push_back (cosNPhi);
454+ values.push_back (sinNPhi);
455+ values.push_back (cosDeltaPhi);
456+ }
457+
458+ if (storeMl) {
459+ values.push_back (outputMl[0 ]);
460+ values.push_back (outputMl[1 ]);
461+ }
462+
463+ if (storeCandEta) {
464+ values.push_back (eta);
465+ }
466+
430467 if (occEstimator != 0 ) {
431- std::vector<int > evtSelFlags = getEventSelectionFlags (hfevselflag);
432- if (storeMl) {
433- if (storeEP) {
434- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp, cosNPhi, sinNPhi, cosDeltaPhi, outputMl[0 ], outputMl[1 ], occupancy,
435- evtSelFlags[0 ], evtSelFlags[1 ], evtSelFlags[2 ], evtSelFlags[3 ], evtSelFlags[4 ]);
436- } else {
437- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp, outputMl[0 ], outputMl[1 ], occupancy,
438- evtSelFlags[0 ], evtSelFlags[1 ], evtSelFlags[2 ], evtSelFlags[3 ], evtSelFlags[4 ]);
439- }
440- } else {
441- if (storeEP) {
442- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp, cosNPhi, sinNPhi, cosDeltaPhi, occupancy,
443- evtSelFlags[0 ], evtSelFlags[1 ], evtSelFlags[2 ], evtSelFlags[3 ], evtSelFlags[4 ]);
444- } else {
445- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp, occupancy,
446- evtSelFlags[0 ], evtSelFlags[1 ], evtSelFlags[2 ], evtSelFlags[3 ], evtSelFlags[4 ]);
447- }
448- }
449- } else {
450- if (storeMl) {
451- if (storeEP) {
452- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp, cosNPhi, sinNPhi, cosDeltaPhi, outputMl[0 ], outputMl[1 ]);
453- } else {
454- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp, outputMl[0 ], outputMl[1 ]);
455- }
456- } else {
457- if (storeEP) {
458- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp, cosNPhi, sinNPhi, cosDeltaPhi);
459- } else {
460- registry.fill (HIST (" hSparseFlowCharm" ), mass, pt, cent, sp);
461- }
462- }
468+ auto evtSelFlags = getEventSelectionFlags (hfevselflag);
469+ values.push_back (occupancy);
470+ values.push_back (evtSelFlags[0 ]);
471+ values.push_back (evtSelFlags[1 ]);
472+ values.push_back (evtSelFlags[2 ]);
473+ values.push_back (evtSelFlags[3 ]);
474+ values.push_back (evtSelFlags[4 ]);
475+ }
476+
477+ if (static_cast <int >(values.size ()) != ndim) {
478+ LOGF (fatal,
479+ " hSparseFlowCharm: number of filled dimensions (%d) "
480+ " does not match THnSparse dimensionality (%d)." ,
481+ static_cast <int >(values.size ()), ndim);
463482 }
483+
484+ hSparse->Fill (values.data ());
464485 }
465486
466487 // / Check if the collision is selected
@@ -555,7 +576,6 @@ struct HfTaskFlowCharmHadrons {
555576 float yQVec = qVecs[1 ];
556577 float const amplQVec = qVecs[2 ];
557578 float const evtPl = epHelper.GetEventPlane (xQVec, yQVec, harmonic);
558- int nProngs = 3 ;
559579
560580 for (const auto & candidate : candidates) {
561581 float massCand = 0 .;
@@ -590,7 +610,6 @@ struct HfTaskFlowCharmHadrons {
590610 }
591611 }
592612 } else if constexpr (std::is_same_v<T1, CandD0Data> || std::is_same_v<T1, CandD0DataWMl>) {
593- nProngs = 2 ;
594613 switch (Channel) {
595614 case DecayChannel::D0ToPiK:
596615 massCand = HfHelper::invMassD0ToPiK (candidate);
@@ -664,27 +683,35 @@ struct HfTaskFlowCharmHadrons {
664683
665684 float ptCand = 0 .;
666685 float phiCand = 0 .;
686+ float etaCand = 0 .;
667687
668688 if constexpr (std::is_same_v<T1, CandXic0Data> || std::is_same_v<T1, CandXic0DataWMl>) {
669689 ptCand = RecoDecay::sqrtSumOfSquares (candidate.pxCharmBaryon (), candidate.pyCharmBaryon ());
670690 phiCand = std::atan2 (candidate.pxCharmBaryon (), candidate.pyCharmBaryon ());
691+ etaCand = candidate.etaBachFromCharmBaryon ();
671692 } else {
672693 ptCand = candidate.pt ();
673694 phiCand = candidate.phi ();
695+ etaCand = candidate.eta ();
674696 }
675697
676- // If TPC is used for the SP estimation, the tracks of the hadron candidate must be removed from the TPC Q vector to avoid double counting
677- if (qvecDetector == QvecEstimator::TPCNeg || qvecDetector == QvecEstimator::TPCPos) {
678- float const ampl = amplQVec - static_cast <float >(nProngs);
679- std::vector<float > tracksQx = {};
680- std::vector<float > tracksQy = {};
698+ // If TPC is used for the SP estimation, the tracks of the hadron candidate must be removed from the corresponding TPC Q vector to avoid self-correlations
699+ if (qvecDetector == QvecEstimator::TPCNeg ||
700+ qvecDetector == QvecEstimator::TPCPos ||
701+ qvecDetector == QvecEstimator::TPCTot) {
702+
703+ std::vector<float > tracksQx;
704+ std::vector<float > tracksQy;
705+
706+ // IMPORTANT: use the ORIGINAL amplitude used to build this Q-vector
681707 if constexpr (std::is_same_v<T1, CandXic0Data> || std::is_same_v<T1, CandXic0DataWMl>) {
682- // std::cout<<candidate.pxProng0()<<std::endl;
683- getQvecXic0Tracks (candidate, tracksQx, tracksQy, ampl);
708+ getQvecXic0Tracks (candidate, tracksQx, tracksQy, amplQVec, static_cast <QvecEstimator>(qvecDetector.value ));
684709 } else {
685- getQvecDtracks<Channel>(candidate, tracksQx, tracksQy, ampl );
710+ getQvecDtracks<Channel>(candidate, tracksQx, tracksQy, amplQVec, static_cast <QvecEstimator>(qvecDetector. value ) );
686711 }
687- for (auto iTrack{0u }; iTrack < tracksQx.size (); ++iTrack) {
712+
713+ // subtract daughters' contribution from the (normalized) Q-vector
714+ for (std::size_t iTrack = 0 ; iTrack < tracksQx.size (); ++iTrack) {
688715 xQVec -= tracksQx[iTrack];
689716 yQVec -= tracksQy[iTrack];
690717 }
@@ -710,7 +737,7 @@ struct HfTaskFlowCharmHadrons {
710737 }
711738 }
712739 if (fillSparse) {
713- fillThn (massCand, ptCand, cent, cosNPhi, sinNPhi, cosDeltaPhi, scalprodCand, outputMl, occupancy, hfevflag);
740+ fillThn (massCand, ptCand, etaCand, cent, cosNPhi, sinNPhi, cosDeltaPhi, scalprodCand, outputMl, occupancy, hfevflag);
714741 }
715742 }
716743 }
0 commit comments