Skip to content

Commit 81202ee

Browse files
AlascrakerZhihe Hanvkucera
authored
[PWGHF,PWGCF,Common] Store K0s DCA to PV and V0 MC origin in FDHfV0 (#18548)
Co-authored-by: Zhihe Han <zhihe.han@cern.ch> Co-authored-by: Vít Kučera <26327373+vkucera@users.noreply.github.com>
1 parent 3513c51 commit 81202ee

4 files changed

Lines changed: 163 additions & 40 deletions

File tree

‎Common/Core/RecoDecay.h‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ struct RecoDecay {
5959
static constexpr int PdgBosonMax{37}; // largest boson (gauge or H) PDG code; o2-linter: disable=pdg/explicit-code (H+ does not have a named constant.)
6060
static constexpr int PdgDivisorMeson{100}; // order of magnitude of the meson PDG codes
6161
static constexpr int PdgDivisorBaryon{1000}; // order of magnitude of the baryon PDG codes
62+
static constexpr int PdgDiquarkSpin0Code{1}; // last digits of a spin-0 diquark PDG code
63+
static constexpr int PdgDiquarkSpin1Code{3}; // last digits of a spin-1 diquark PDG code
6264

6365
// Auxiliary functions
6466

@@ -1179,6 +1181,31 @@ struct RecoDecay {
11791181
return OriginType::None;
11801182
}
11811183

1184+
/// Check whether the PDG code corresponds to a diquark state (e.g. 4101, 4201, 5101).
1185+
/// Diquarks are constituents used by the generators during the hadronisation and are not
1186+
/// hadrons. A diquark carrying a charm or beauty quark has a PDG code that satisfies the
1187+
/// pdg / 1000 == 4 or 5 condition used to identify charm and beauty baryons in
1188+
/// getParticleOrigin, so such a state would be misidentified as a heavy-flavour baryon there.
1189+
/// This check therefore validates the diquark structure: two quark flavours in
1190+
/// PDG_t::kDown..kTop (any quark pair, not only a heavy-flavour one), the first flavour not
1191+
/// smaller than the second, and a spin-0 or spin-1 code.
1192+
/// \param pdgCode PDG code of the particle
1193+
/// \return true if the PDG code belongs to a diquark state
1194+
static bool isDiquark(const int pdgCode)
1195+
{
1196+
// a diquark PDG code is composed as pdgQuark1 * PdgDivisorBaryon + pdgQuark2 * PdgDivisorMeson
1197+
// + spinCode, with pdgQuark1 >= pdgQuark2 the quark flavours and spinCode either
1198+
// PdgDiquarkSpin0Code or PdgDiquarkSpin1Code
1199+
const int absPdgCode = std::abs(pdgCode);
1200+
const int spinCode = absPdgCode % PdgDivisorMeson;
1201+
if (spinCode != PdgDiquarkSpin0Code && spinCode != PdgDiquarkSpin1Code) {
1202+
return false;
1203+
}
1204+
const int pdgQuark1 = absPdgCode / PdgDivisorBaryon;
1205+
const int pdgQuark2 = (absPdgCode % PdgDivisorBaryon) / PdgDivisorMeson;
1206+
return pdgQuark1 >= PDG_t::kDown && pdgQuark1 <= PDG_t::kTop && pdgQuark2 >= PDG_t::kDown && pdgQuark2 <= PDG_t::kTop && pdgQuark1 >= pdgQuark2;
1207+
}
1208+
11821209
/// based on getCharmHardronOrigin in order to extend general particle
11831210
/// Finding the origin (from charm hadronisation or beauty-hadron decay) of paritcle (b, c and others)
11841211
/// \param particlesMC table with MC particles
@@ -1221,6 +1248,17 @@ struct RecoDecay {
12211248
// Check status code
12221249
// auto motherStatusCode = std::abs(mother.getGenStatusCode());
12231250
auto pdgParticleIMother = std::abs(mother.pdgCode()); // PDG code of the mother
1251+
// in the hadron-level mode, stop the walk when a parton or a diquark is reached: no
1252+
// heavy-flavour hadron can be found above the partonic level, and a diquark is not a
1253+
// hadron although its PDG code (e.g. 4101, 4201, 5101) satisfies pdg / 1000 == 4 or 5
1254+
// and would be misidentified as a charm/beauty baryon below. In the parton-level mode
1255+
// the walk has to continue through diquarks, since a charm or beauty quark can still be
1256+
// found above them.
1257+
if (!searchUpToQuark &&
1258+
(isDiquark(pdgParticleIMother) || pdgParticleIMother <= PdgQuarkMax ||
1259+
(pdgParticleIMother >= PdgBosonMin && pdgParticleIMother <= PdgBosonMax))) {
1260+
break;
1261+
}
12241262
// Check mother's PDG code.
12251263
// printf("getMother: ");
12261264
// for (int i = stage; i < 0; i++) // Indent to make the tree look nice.

‎PWGCF/DataModel/FemtoDerived.h‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta,
324324
RecoDecayPtEtaPhi::pVector(pt1, eta1, phi1),
325325
RecoDecayPtEtaPhi::pVector(pt2, eta2, phi2))); }); //! Eta distribution of charm hadron
326326

327+
DECLARE_SOA_COLUMN(K0sDcaToPv, k0sDcaToPv, float); //! DCA of the K0s candidate to the primary vertex (cm)
328+
DECLARE_SOA_COLUMN(OriginMcV0, originMcV0, int); //! MC truth origin of the V0 candidate, see ParticleOriginMCTruth
329+
327330
} // namespace fdhf
328331

329332
namespace fdhf_2prong
@@ -633,7 +636,9 @@ DECLARE_SOA_TABLE(FDHfV0, "AOD", "FDHFV0", //! table to store results for HF fem
633636
track::TPCNClsFindable,
634637
femtodreamparticle::TPCNClsCrossedRows,
635638
femtodreamparticle::TPCNSigmaPr,
636-
femtodreamparticle::TOFNSigmaPr);
639+
femtodreamparticle::TOFNSigmaPr,
640+
fdhf::K0sDcaToPv,
641+
fdhf::OriginMcV0);
637642

638643
DECLARE_SOA_TABLE(FDHfColl, "AOD", "FDHFCOLL", //! table to store results for HF femtoscopy
639644
fdhf::GIndexCol,
@@ -745,6 +750,7 @@ enum ParticleOriginMCTruth {
745750
kElse, //! none of the above; (NOTE: used to catch bugs. will be removed once MC usage is properly validated)
746751
kSecondaryFromHF, //! Kaon from a charm- or beauty-hadron decay chain
747752
kSecondaryFromLF, //! Kaon from a light-flavour decay without a heavy-flavour ancestor
753+
kSecondaryFromPhi, //! Kaon from a phi decay chain (phi -> K0/K0bar -> K0S)
748754
kNOriginMCTruthTypes
749755
};
750756

@@ -766,7 +772,8 @@ static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes
766772
"_SecondaryDaughterXistarMinus",
767773
"_Else",
768774
"_SecondaryFromHF",
769-
"_SecondaryFromLF"};
775+
"_SecondaryFromLF",
776+
"_SecondaryFromPhi"};
770777

771778
/// Distinguished between reconstructed and truth
772779
enum MCType {

‎PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx‎

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "PWGHF/Utils/utilsEvSelHf.h"
3333
#include "PWGLF/DataModel/LFStrangenessTables.h"
3434

35+
#include "Common/Core/RecoDecay.h"
3536
#include "Common/DataModel/Centrality.h"
3637
#include "Common/DataModel/EventSelection.h"
3738
#include "Common/DataModel/Multiplicity.h"
@@ -59,6 +60,7 @@
5960

6061
#include <TH1.h>
6162
#include <TMCProcess.h>
63+
#include <TPDGCode.h>
6264

6365
#include <array>
6466
#include <chrono>
@@ -494,8 +496,47 @@ struct HfProducerCharmHadronsV0FemtoDream {
494496
}
495497
}
496498

499+
/// Classify the MC origin of a V0 candidate.
500+
/// The heavy-flavour feed-down is identified with RecoDecay::getParticleOrigin, which also
501+
/// covers charm- and beauty-hadron decays flagged as physical primaries. The feed-down from
502+
/// a phi decay is light-flavour and kept as a separate class, since the relatively long phi
503+
/// lifetime makes it a distinct source.
504+
/// \param mcParticles table of the MC particles
505+
/// \param particleMc MC particle matched to the V0 candidate
506+
/// \param isFromPhi true if the K0S comes from a phi decay chain
507+
/// \return the origin according to ParticleOriginMCTruth (kPrimary, kMaterial,
508+
/// kSecondaryFromHF, kSecondaryFromLF or kSecondaryFromPhi)
509+
template <typename T>
510+
static aod::femtodreamMCparticle::ParticleOriginMCTruth getK0sOrigin(T const& mcParticles, typename T::iterator const& particleMc, const bool isFromPhi)
511+
{
512+
using namespace o2::aod::femtodreamMCparticle;
513+
constexpr int GenFromTransport = -1;
514+
const auto process = particleMc.getProcess();
515+
// hadronic interaction in the detector material
516+
if (process == TMCProcess::kPHInhelastic && particleMc.getGenStatusCode() == GenFromTransport) {
517+
return kMaterial;
518+
}
519+
// charm- or beauty-hadron anywhere in the mother chain (feed-down can be a physical primary)
520+
if (RecoDecay::getParticleOrigin(mcParticles, particleMc, false) != RecoDecay::OriginType::None) {
521+
return kSecondaryFromHF;
522+
}
523+
// phi -> K0/K0bar -> K0S is light-flavour feed-down, but kept as a separate class because
524+
// the relatively long phi lifetime makes it a distinct source (also flagged as a physical primary)
525+
if (isFromPhi) {
526+
return kSecondaryFromPhi;
527+
}
528+
if (particleMc.isPhysicalPrimary()) {
529+
return kPrimary;
530+
}
531+
// weak decay of a light-flavour hadron during the transport
532+
if (process == TMCProcess::kPDecay && particleMc.getGenStatusCode() == GenFromTransport && particleMc.has_mothers()) {
533+
return kSecondaryFromLF;
534+
}
535+
return kElse;
536+
}
537+
497538
template <typename CollisionType, typename ParticleType>
498-
void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype)
539+
void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype, aod::McParticles const& mcParticles)
499540
{
500541
if (particle.has_mcParticle()) {
501542
// get corresponding MC particle and its info
@@ -510,6 +551,19 @@ struct HfProducerCharmHadronsV0FemtoDream {
510551
float etaPos{-1.f}, etaNeg{-1.f};
511552
float phiPos{-1.f}, phiNeg{-1.f};
512553
int pdgCodePos{0}, pdgCodeNeg{0};
554+
// phi -> K0/K0bar -> K0s (or phi -> K0s directly); the heavy-flavour feed-down is
555+
// handled in getK0sOrigin through RecoDecay::getParticleOrigin
556+
bool isFromPhi = false;
557+
if (fdparttype == o2::aod::femtodreamparticle::ParticleType::kV0 && !motherparticlesMc.empty()) {
558+
const auto absPdgMother = std::abs(motherparticlesMc.front().pdgCode());
559+
if (absPdgMother == o2::constants::physics::Pdg::kPhi) {
560+
isFromPhi = true;
561+
} else if (absPdgMother == kK0) {
562+
const auto& grandMothersMc = motherparticlesMc.front().template mothers_as<aod::McParticles>();
563+
isFromPhi = !grandMothersMc.empty() && std::abs(grandMothersMc.front().pdgCode()) == o2::constants::physics::Pdg::kPhi;
564+
}
565+
}
566+
513567
// check pdg code
514568
// if this fails, the particle is a fake
515569
if (std::abs(pdgCode) == std::abs(v0PDGCode.value)) {
@@ -518,6 +572,11 @@ struct HfProducerCharmHadronsV0FemtoDream {
518572
if ((col.has_mcCollision() && (particleMc.mcCollisionId() != col.mcCollisionId())) || !col.has_mcCollision()) {
519573
particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kWrongCollision;
520574
// check if particle is primary
575+
} else if (fdparttype == o2::aod::femtodreamparticle::ParticleType::kV0) {
576+
particleOrigin = getK0sOrigin(mcParticles, particleMc, isFromPhi);
577+
if (!motherparticlesMc.empty()) {
578+
pdgCodeMother = motherparticlesMc.front().pdgCode();
579+
}
521580
} else if (particleMc.isPhysicalPrimary()) {
522581
particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kPrimary;
523582
// check if particle is secondary
@@ -612,7 +671,7 @@ struct HfProducerCharmHadronsV0FemtoDream {
612671
}
613672

614673
template <bool IsMc = false, typename V0Type, typename CollisionType, typename TrackType>
615-
bool fillV0sForCharmHadron(CollisionType const& col, V0Type const& fullV0s, TrackType const& /*tracks*/)
674+
bool fillV0sForCharmHadron(CollisionType const& col, V0Type const& fullV0s, TrackType const& /*tracks*/, aod::McParticles const* mcParticles = nullptr)
616675
{
617676
const bool isK0S = (selectionFlagV0 == V0Channel::K0S);
618677
const bool isLambda = (selectionFlagV0 == V0Channel::Lambda);
@@ -724,15 +783,15 @@ struct HfProducerCharmHadronsV0FemtoDream {
724783
}
725784

726785
if constexpr (IsMc) {
727-
fillMcParticle(col, v0, o2::aod::femtodreamparticle::ParticleType::kV0);
786+
fillMcParticle(col, v0, o2::aod::femtodreamparticle::ParticleType::kV0, *mcParticles);
728787
}
729788
fIsV0Filled = true;
730789
}
731790
return fIsV0Filled;
732791
}
733792

734793
template <DecayChannel Channel, bool IsMc, bool UseCharmMl, typename V0Type, typename CollisionType, typename TrackType, typename CandType>
735-
void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, V0Type const& fullV0s, CandType const& candidates)
794+
void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, V0Type const& fullV0s, CandType const& candidates, aod::McParticles const* mcParticles = nullptr)
736795
{
737796
const auto vtxZ = col.posZ();
738797
const auto sizeCand = candidates.size();
@@ -1019,7 +1078,7 @@ struct HfProducerCharmHadronsV0FemtoDream {
10191078
fillTable(2, candidate.isSelDstarToD0Pi(), outputMlDstar.at(0), outputMlDstar.at(1), outputMlDstar.at(2));
10201079
}
10211080
}
1022-
isV0Filled = fillV0sForCharmHadron<IsMc>(col, fullV0s, tracks);
1081+
isV0Filled = fillV0sForCharmHadron<IsMc>(col, fullV0s, tracks, mcParticles);
10231082
aod::femtodreamcollision::BitMaskType bitV0 = 0;
10241083
if (isV0Filled) {
10251084
bitV0 |= 1 << 0;
@@ -1248,26 +1307,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
12481307
aod::BCsWithTimestamps const&,
12491308
FemtoHFMcTracks const& tracks,
12501309
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1251-
aod::McParticles const&,
1310+
aod::McParticles const& mcParticles,
12521311
CandidateD0Mc const& candidates)
12531312
{
12541313
// get magnetic field for run
12551314
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1256-
fillCharmHadronTable<DecayChannel::D0ToPiK, true, false>(col, tracks, fullV0s, candidates);
1315+
fillCharmHadronTable<DecayChannel::D0ToPiK, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
12571316
}
12581317
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcD0ToPiK, "Provide Mc for D0ToPiK", false);
12591318

12601319
void processMcD0ToPiKWithML(FemtoFullCollisionMc const& col,
12611320
aod::BCsWithTimestamps const&,
12621321
FemtoHFMcTracks const& tracks,
12631322
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1264-
aod::McParticles const&,
1323+
aod::McParticles const& mcParticles,
12651324
soa::Join<CandidateD0Mc,
12661325
aod::HfMlD0> const& candidates)
12671326
{
12681327
// get magnetic field for run
12691328
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1270-
fillCharmHadronTable<DecayChannel::D0ToPiK, true, true>(col, tracks, fullV0s, candidates);
1329+
fillCharmHadronTable<DecayChannel::D0ToPiK, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
12711330
}
12721331
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcD0ToPiKWithML, "Provide Mc for D0ToPiK with ml", false);
12731332

@@ -1307,26 +1366,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
13071366
aod::BCsWithTimestamps const&,
13081367
FemtoHFMcTracks const& tracks,
13091368
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1310-
aod::McParticles const&,
1369+
aod::McParticles const& mcParticles,
13111370
CandidateDstarMc const& candidates)
13121371
{
13131372
// get magnetic field for run
13141373
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1315-
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, false>(col, tracks, fullV0s, candidates);
1374+
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
13161375
}
13171376
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDstarToD0Pi, "Provide Mc for DstarToD0Pi", false);
13181377

13191378
void processMcDstarToD0PiWithML(FemtoFullCollisionMc const& col,
13201379
aod::BCsWithTimestamps const&,
13211380
FemtoHFMcTracks const& tracks,
13221381
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1323-
aod::McParticles const&,
1382+
aod::McParticles const& mcParticles,
13241383
soa::Join<CandidateDstarMc,
13251384
aod::HfMlDstarToD0Pi> const& candidates)
13261385
{
13271386
// get magnetic field for run
13281387
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1329-
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, true>(col, tracks, fullV0s, candidates);
1388+
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
13301389
}
13311390
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDstarToD0PiWithML, "Provide Mc for DstarToD0Pi with ml", false);
13321391

@@ -1368,26 +1427,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
13681427
aod::BCsWithTimestamps const&,
13691428
FemtoHFMcTracks const& tracks,
13701429
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1371-
aod::McParticles const&,
1430+
aod::McParticles const& mcParticles,
13721431
CandidateDplusMc const& candidates)
13731432
{
13741433
// get magnetic field for run
13751434
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1376-
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, false>(col, tracks, fullV0s, candidates);
1435+
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
13771436
}
13781437
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDplusToPiKPi, "Provide Mc for DplusToPiKPi", false);
13791438

13801439
void processMcDplusToPiKPiWithML(FemtoFullCollisionMc const& col,
13811440
aod::BCsWithTimestamps const&,
13821441
FemtoHFMcTracks const& tracks,
13831442
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1384-
aod::McParticles const&,
1443+
aod::McParticles const& mcParticles,
13851444
soa::Join<CandidateDplusMc,
13861445
aod::HfMlDplusToPiKPi> const& candidates)
13871446
{
13881447
// get magnetic field for run
13891448
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1390-
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, true>(col, tracks, fullV0s, candidates);
1449+
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
13911450
}
13921451
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDplusToPiKPiWithML, "Provide Mc for DplusToPiKPi with ml", false);
13931452

@@ -1429,26 +1488,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
14291488
aod::BCsWithTimestamps const&,
14301489
FemtoHFMcTracks const& tracks,
14311490
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1432-
aod::McParticles const&,
1491+
aod::McParticles const& mcParticles,
14331492
CandidateLcMc const& candidates)
14341493
{
14351494
// get magnetic field for run
14361495
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1437-
fillCharmHadronTable<DecayChannel::LcToPKPi, true, false>(col, tracks, fullV0s, candidates);
1496+
fillCharmHadronTable<DecayChannel::LcToPKPi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
14381497
}
14391498
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcLcToPKPi, "Provide Mc for lctopkpi", false);
14401499

14411500
void processMcLcToPKPiWithML(FemtoFullCollisionMc const& col,
14421501
aod::BCsWithTimestamps const&,
14431502
FemtoHFMcTracks const& tracks,
14441503
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1445-
aod::McParticles const&,
1504+
aod::McParticles const& mcParticles,
14461505
soa::Join<CandidateLcMc,
14471506
aod::HfMlLcToPKPi> const& candidates)
14481507
{
14491508
// get magnetic field for run
14501509
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1451-
fillCharmHadronTable<DecayChannel::LcToPKPi, true, true>(col, tracks, fullV0s, candidates);
1510+
fillCharmHadronTable<DecayChannel::LcToPKPi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
14521511
}
14531512
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcLcToPKPiWithML, "Provide Mc for lctopkpi with ml", false);
14541513

0 commit comments

Comments
 (0)