@@ -63,6 +63,7 @@ using namespace o2::framework::expressions;
6363using namespace o2 ::hf_centrality;
6464using namespace o2 ::hf_occupancy;
6565using namespace o2 ::hf_evsel;
66+ using namespace o2 ::constants::physics;
6667
6768// / Λc± → p± K∓ π± analysis task
6869struct HfTaskLc {
@@ -74,7 +75,7 @@ struct HfTaskLc {
7475 Configurable<bool > fillTHn{" fillTHn" , false , " fill THn" };
7576 Configurable<bool > storeOccupancy{" storeOccupancy" , true , " Flag to store occupancy information" };
7677 Configurable<int > occEstimator{" occEstimator" , 2 , " Occupancy estimation (None: 0, ITS: 1, FT0C: 2)" };
77- Configurable<bool > storeProperLifetime{ " storeProperLifetime " , false , " Flag to store proper lifetime " };
78+ Configurable<bool > storeProperDecayTime{ " storeProperDecayTime " , false , " Flag to store proper decay time " };
7879 // CCDB configuration
7980 Configurable<std::string> ccdbUrl{" ccdbUrl" , " http://alice-ccdb.cern.ch" , " url of the ccdb repository" };
8081 Configurable<std::string> ccdbPathGrp{" ccdbPathGrp" , " GLO/GRP/GRP" , " Path of the grp file (Run 2)" };
@@ -117,16 +118,9 @@ struct HfTaskLc {
117118 ConfigurableAxis thnConfigAxisGenPtB{" thnConfigAxisGenPtB" , {1000 , 0 , 100 }, " Gen Pt B" };
118119 ConfigurableAxis thnConfigAxisNumPvContr{" thnConfigAxisNumPvContr" , {200 , -0.5 , 199.5 }, " Number of PV contributors" };
119120 ConfigurableAxis thnConfigAxisOccupancy{" thnConfigAxisOccupancy" , {14 , 0 , 14000 }, " axis for centrality" };
120- ConfigurableAxis thnConfigAxisProperLifetime{ " thnConfigAxisProperLifetime " , {200 , 0 , 2 }, " Proper lifetime , ps" };
121+ ConfigurableAxis thnConfigAxisProperDecayTime{ " thnConfigAxisProperDecayTime " , {200 , 0 , 2 }, " Proper decay time , ps" };
121122 HistogramRegistry registry{" registry" , {}};
122123
123- // Factors for conversion between units
124- constexpr static float CtToProperLifetimePs = 1 .f / o2::constants::physics::LightSpeedCm2PS;
125- constexpr static float NanoToPico = 1000 .f;
126- // Names of folders and suffixes for MC signal histograms
127- constexpr static std::string_view SignalFolders[] = {" signal" , " prompt" , " nonprompt" };
128- constexpr static std::string_view SignalSuffixes[] = {" " , " Prompt" , " NonPrompt" };
129-
130124 enum MlClasses : int {
131125 MlClassBackground = 0 ,
132126 MlClassPrompt,
@@ -137,9 +131,16 @@ struct HfTaskLc {
137131 enum SignalClasses : int {
138132 Signal = 0 ,
139133 Prompt,
140- NonPrompt
134+ NonPrompt,
135+ NumberOfSignalClasses
141136 };
142137
138+ // Factor for conversion between units
139+ constexpr static float CtToProperDecayTimePs = 1 .f / o2::constants::physics::LightSpeedCm2PS;
140+ // Names of folders and suffixes for MC signal histograms
141+ constexpr static std::array<std::string_view, NumberOfSignalClasses> SignalFolders = {" signal" , " prompt" , " nonprompt" };
142+ constexpr static std::array<std::string_view, NumberOfSignalClasses> SignalSuffixes = {" " , " Prompt" , " NonPrompt" };
143+
143144 void init (InitContext&)
144145 {
145146 const std::array<bool , 12 > doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, doprocessDataWithMl, doprocessDataWithMlWithFT0C, doprocessDataWithMlWithFT0M, doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M, doprocessMcWithMl, doprocessMcWithMlWithFT0C, doprocessMcWithMlWithFT0M};
@@ -186,8 +187,8 @@ struct HfTaskLc {
186187 addHistogramsRec (" hDecLength" , " decay length (cm)" , " entries" , {HistType::kTH1F , {{400 , 0 ., 1 .}}});
187188 // / decay length xy candidate
188189 addHistogramsRec (" hDecLengthxy" , " decay length xy (cm)" , " entries" , {HistType::kTH1F , {{400 , 0 ., 1 .}}});
189- // / proper lifetime
190- addHistogramsRec (" hCt" , " proper lifetime (#Lambda_{c}) * #it{c} (cm)" , " entries" , {HistType::kTH1F , {{100 , 0 ., 0.2 }}});
190+ // / proper decay time
191+ addHistogramsRec (" hCt" , " proper decay time (#Lambda_{c}) * #it{c} (cm)" , " entries" , {HistType::kTH1F , {{100 , 0 ., 0.2 }}});
191192 // / cosine of pointing angle
192193 addHistogramsRec (" hCPA" , " cosine of pointing angle" , " entries" , {HistType::kTH1F , {{110 , -1.1 , 1.1 }}});
193194 // / cosine of pointing angle xy
@@ -219,8 +220,8 @@ struct HfTaskLc {
219220 // / decay length xy candidate
220221 addHistogramsRec (" hDecLengthxyVsPt" , " decay length xy (cm)" , " #it{p}_{T} (GeV/#it{c})" , {HistType::kTH2F , {{400 , 0 ., 1 .}, {vbins}}});
221222
222- // / proper lifetime
223- addHistogramsRec (" hCtVsPt" , " proper lifetime (#Lambda_{c}) * #it{c} (cm)" , " #it{p}_{T} (GeV/#it{c})" , {HistType::kTH2F , {{100 , 0 ., 0.2 }, {vbins}}});
223+ // / proper decay time
224+ addHistogramsRec (" hCtVsPt" , " proper decay time (#Lambda_{c}) * #it{c} (cm)" , " #it{p}_{T} (GeV/#it{c})" , {HistType::kTH2F , {{100 , 0 ., 0.2 }, {vbins}}});
224225
225226 // / cosine of pointing angle
226227 addHistogramsRec (" hCPAVsPt" , " cosine of pointing angle" , " #it{p}_{T} (GeV/#it{c})" , {HistType::kTH2F , {{110 , -1.1 , 1.1 }, {vbins}}});
@@ -268,7 +269,8 @@ struct HfTaskLc {
268269 const AxisSpec thnAxisPtB{thnConfigAxisGenPtB, " #it{p}_{T}^{B} (GeV/#it{c})" };
269270 const AxisSpec thnAxisTracklets{thnConfigAxisNumPvContr, " Number of PV contributors" };
270271 const AxisSpec thnAxisOccupancy{thnConfigAxisOccupancy, " Occupancy" };
271- const AxisSpec thnAxisProperLifetime{thnConfigAxisProperLifetime, " T_{proper} (ps)" };
272+ const AxisSpec thnAxisProperDecayTime{thnConfigAxisProperDecayTime, " #it{t}_{proper} (ps)" };
273+ const AxisSpec thnAxisProperDecayTimeGen{thnConfigAxisProperDecayTime, " #it{t}_{proper, gen} (ps)" };
272274
273275 bool const isDataWithMl = doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M;
274276 bool const isMcWithMl = doprocessMcWithMl || doprocessMcWithMlWithFT0C || doprocessMcWithMlWithFT0M;
@@ -300,10 +302,13 @@ struct HfTaskLc {
300302 }
301303 }
302304 }
303- if (storeProperLifetime ) {
305+ if (storeProperDecayTime ) {
304306 for (const auto & axes : std::array<std::vector<AxisSpec>*, 3 >{&axesWithBdt, &axesStd, &axesGen}) {
305307 if (!axes->empty ()) {
306- axes->push_back (thnAxisProperLifetime);
308+ axes->push_back (thnAxisProperDecayTime);
309+ if (!isData && axes != &axesGen) {
310+ axes->push_back (thnAxisProperDecayTimeGen);
311+ }
307312 }
308313 }
309314 }
@@ -334,13 +339,34 @@ struct HfTaskLc {
334339 return o2::hf_centrality::getCentralityColl<Coll>(collision);
335340 }
336341
342+ // / Evaluate decay time of generated particle
343+ // / \param mcParticleProng0 one of generated particle's daughters
344+ // / \param motherParticle generated particle
345+ // / \return decay time in picoseconds. For nonprompt particles it is evaluated as if it was prompt (as it is calculated for data)
346+ float evaluateMcGenDecayTime (const McParticles3ProngMatched::iterator& mcParticleProng0, const McParticles3ProngMatched::iterator& motherParticle)
347+ {
348+ const auto mcCollision = motherParticle.template mcCollision_as <aod::McCollisions>();
349+ const float pMother = motherParticle.p ();
350+ const float pvX = mcCollision.posX ();
351+ const float pvY = mcCollision.posY ();
352+ const float pvZ = mcCollision.posZ ();
353+ const float svX = mcParticleProng0.vx ();
354+ const float svY = mcParticleProng0.vy ();
355+ const float svZ = mcParticleProng0.vz ();
356+
357+ const float decayLength = static_cast <float >(RecoDecay::distance (std::array<float , 3 >{svX, svY, svZ}, std::array<float , 3 >{pvX, pvY, pvZ}));
358+ const float properDecayTime = decayLength * static_cast <float >(MassLambdaCPlus) / LightSpeedCm2PS / pMother;
359+
360+ return properDecayTime;
361+ }
362+
337363 // / Helper function for filling MC reconstructed histograms for prompt, nonpromt and common (signal)
338364 // / \param candidate is a reconstructed candidate
339365 // / \tparam SignalType is an enum defining which histogram in which folder (signal, prompt or nonpromt) to fill
340366 template <int SignalType, typename CandidateType>
341367 void fillHistogramsRecSig (CandidateType const & candidate)
342368 {
343- const auto & mcParticleProng0 = candidate.template prong0_as <aod::TracksWMc>().template mcParticle_as <soa::Join<aod::McParticles, aod::HfCand3ProngMcGen> >();
369+ const auto & mcParticleProng0 = candidate.template prong0_as <aod::TracksWMc>().template mcParticle_as <McParticles3ProngMatched >();
344370 const auto pdgCodeProng0 = std::abs (mcParticleProng0.pdgCode ());
345371 if ((candidate.isSelLcToPKPi () >= selectionFlagLc) && pdgCodeProng0 == kProton ) {
346372 registry.fill (HIST (" MC/reconstructed/" ) + HIST (SignalFolders[SignalType]) + HIST (" /hMassRecSig" ) + HIST (SignalSuffixes[SignalType]), HfHelper::invMassLcToPKPi (candidate));
@@ -385,8 +411,8 @@ struct HfTaskLc {
385411
386412 // / Fill MC histograms at reconstruction level
387413 // / \tparam FillMl switch to fill ML histograms
388- template <bool FillMl, typename CollType, typename CandLcMcRec, typename CandLcMcGen >
389- void fillHistosMcRec (CollType const & collision, CandLcMcRec const & candidates, CandLcMcGen const & mcParticles)
414+ template <bool FillMl, typename CollType, typename CandLcMcRec>
415+ void fillHistosMcRec (CollType const & collision, CandLcMcRec const & candidates, McParticles3ProngMatched const & mcParticles)
390416 {
391417 const auto thisCollId = collision.globalIndex ();
392418 const auto & groupedLcCandidates = candidates.sliceBy (candLcPerCollision, thisCollId);
@@ -403,7 +429,7 @@ struct HfTaskLc {
403429
404430 if (std::abs (candidate.flagMcMatchRec ()) == hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) {
405431 // Get the corresponding MC particle.
406- const auto & mcParticleProng0 = candidate.template prong0_as <aod::TracksWMc>().template mcParticle_as <soa::Join<aod::McParticles, aod::HfCand3ProngMcGen> >();
432+ const auto & mcParticleProng0 = candidate.template prong0_as <aod::TracksWMc>().template mcParticle_as <McParticles3ProngMatched >();
407433 const auto pdgCodeProng0 = std::abs (mcParticleProng0.pdgCode ());
408434 const auto indexMother = RecoDecay::getMother (mcParticles, mcParticleProng0, o2::constants::physics::Pdg::kLambdaCPlus , true );
409435 const auto particleMother = mcParticles.rawIteratorAt (indexMother);
@@ -420,6 +446,8 @@ struct HfTaskLc {
420446 const auto numPvContributors = collision.numContrib ();
421447 const auto ptRecB = candidate.ptBhadMotherPart ();
422448
449+ const float properDecayTimeGen = evaluateMcGenDecayTime (mcParticleProng0, particleMother);
450+
423451 // / MC reconstructed signal
424452 fillHistogramsRecSig<Signal>(candidate);
425453
@@ -438,7 +466,7 @@ struct HfTaskLc {
438466 occ = o2::hf_occupancy::getOccupancyColl (collision, occEstimator);
439467 }
440468 double outputBkg (-1 ), outputPrompt (-1 ), outputFD (-1 );
441- const float properLifetime = HfHelper::ctLc (candidate) * CtToProperLifetimePs ;
469+ const float properDecayTime = HfHelper::ctLc (candidate) * CtToProperDecayTimePs ;
442470
443471 auto fillTHnRecSig = [&](bool isPKPi) {
444472 const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi (candidate) : HfHelper::invMassLcToPiKP (candidate);
@@ -461,8 +489,9 @@ struct HfTaskLc {
461489 if (storeOccupancy && occEstimator != o2::hf_occupancy::OccupancyEstimator::None) {
462490 valuesToFill.push_back (occ);
463491 }
464- if (storeProperLifetime) {
465- valuesToFill.push_back (properLifetime);
492+ if (storeProperDecayTime) {
493+ valuesToFill.push_back (properDecayTime);
494+ valuesToFill.push_back (properDecayTimeGen);
466495 }
467496 if constexpr (FillMl) {
468497 registry.get <THnSparse>(HIST (" hnLcVarsWithBdt" ))->Fill (valuesToFill.data ());
@@ -485,8 +514,8 @@ struct HfTaskLc {
485514 // / Helper function for filling MC generated histograms for prompt, nonpromt and common (signal)
486515 // / \param particle is a generated particle
487516 // / \tparam SignalType is an enum defining which histogram in which folder (signal, prompt or nonpromt) to fill
488- template <int SignalType, typename ParticleType >
489- void fillHistogramsGen (ParticleType const & particle)
517+ template <int SignalType>
518+ void fillHistogramsGen (McParticles3ProngMatched::iterator const & particle)
490519 {
491520 registry.fill (HIST (" MC/generated/" ) + HIST (SignalFolders[SignalType]) + HIST (" /hPtGen" ) + HIST (SignalSuffixes[SignalType]), particle.pt ());
492521 registry.fill (HIST (" MC/generated/" ) + HIST (SignalFolders[SignalType]) + HIST (" /hEtaGen" ) + HIST (SignalSuffixes[SignalType]), particle.eta ());
@@ -498,8 +527,8 @@ struct HfTaskLc {
498527 }
499528
500529 // / Fill MC histograms at generated level
501- template <typename CandLcMcGen, typename Coll>
502- void fillHistosMcGen (CandLcMcGen const & mcParticles, Coll const & recoCollisions)
530+ template <typename Coll>
531+ void fillHistosMcGen (McParticles3ProngMatched const & mcParticles, Coll const & recoCollisions)
503532 {
504533 // MC gen.
505534 for (const auto & particle : mcParticles) {
@@ -522,10 +551,9 @@ struct HfTaskLc {
522551 occ = o2::hf_occupancy::getOccupancyGenColl (recoCollsPerMcColl, occEstimator);
523552 }
524553
525- const auto & mcDaughter0 = particle.template daughters_as <soa::Join<aod::McParticles, aod::HfCand3ProngMcGen>>().begin ();
526- const float p2m = particle.p () / o2::constants::physics::MassLambdaCPlus;
527- const float gamma = std::sqrt (1 + p2m * p2m); // mother's particle Lorentz factor
528- const float properLifetime = mcDaughter0.vt () * NanoToPico / gamma; // from ns to ps * from lab time to proper time
554+ const auto mcDaughter0 = particle.template daughters_as <McParticles3ProngMatched>().begin ();
555+
556+ const float properDecayTime = evaluateMcGenDecayTime (mcDaughter0, particle);
529557
530558 fillHistogramsGen<Signal>(particle);
531559
@@ -537,8 +565,8 @@ struct HfTaskLc {
537565 if (storeOccupancy && occEstimator != o2::hf_occupancy::OccupancyEstimator::None) {
538566 valuesToFill.push_back (occ);
539567 }
540- if (storeProperLifetime ) {
541- valuesToFill.push_back (properLifetime );
568+ if (storeProperDecayTime ) {
569+ valuesToFill.push_back (properDecayTime );
542570 }
543571 registry.get <THnSparse>(HIST (" hnLcVarsGen" ))->Fill (valuesToFill.data ());
544572 }
@@ -631,7 +659,7 @@ struct HfTaskLc {
631659 occ = o2::hf_occupancy::getOccupancyColl (collision, occEstimator);
632660 }
633661 double outputBkg (-1 ), outputPrompt (-1 ), outputFD (-1 );
634- const float properLifetime = HfHelper::ctLc (candidate) * CtToProperLifetimePs ;
662+ const float properDecayTime = HfHelper::ctLc (candidate) * CtToProperDecayTimePs ;
635663
636664 auto fillTHnData = [&](bool isPKPi) {
637665 const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi (candidate) : HfHelper::invMassLcToPiKP (candidate);
@@ -654,8 +682,8 @@ struct HfTaskLc {
654682 if (storeOccupancy && occEstimator != o2::hf_occupancy::OccupancyEstimator::None) {
655683 valuesToFill.push_back (occ);
656684 }
657- if (storeProperLifetime ) {
658- valuesToFill.push_back (properLifetime );
685+ if (storeProperDecayTime ) {
686+ valuesToFill.push_back (properDecayTime );
659687 }
660688 if constexpr (FillMl) {
661689 registry.get <THnSparse>(HIST (" hnLcVarsWithBdt" ))->Fill (valuesToFill.data ());
@@ -687,10 +715,10 @@ struct HfTaskLc {
687715
688716 // / Run the analysis on MC data
689717 // / \tparam FillMl switch to fill ML histograms
690- template <bool FillMl, typename CollType, typename CandType, typename CandLcMcGen >
718+ template <bool FillMl, typename CollType, typename CandType>
691719 void runAnalysisPerCollisionMc (CollType const & collisions,
692720 CandType const & candidates,
693- CandLcMcGen const & mcParticles)
721+ McParticles3ProngMatched const & mcParticles)
694722 {
695723 for (const auto & collision : collisions) {
696724 // MC Rec.
0 commit comments