Skip to content

Commit 6e4c8a6

Browse files
author
Zhihe Han
committed
[PWGHF] Fill the V0 MC origin with HF, LF and phi secondary classes
- reuse RecoDecay::getParticleOrigin (as done in #18481) instead of an ad-hoc first-digit PDG scan, so the V0 origin is filled with kPrimary, kMaterial, kSecondaryFromHF and kSecondaryFromLF - add kSecondaryFromPhi: the phi chain (phi -> K0/K0bar -> K0s) is light-flavour feed-down, but it is kept as a separate class because the relatively long phi lifetime makes it a distinct source that should not be mixed with other light-flavour feed-down - add RecoDecay::isDiquark and skip diquark states in RecoDecay::getParticleOrigin: their PDG codes (4101, 4201, 5101, ...) satisfy pdg / 1000 == 4 or 5 and would otherwise be taken as charm/beauty baryons - use o2::constants::physics::Pdg::kPhi and kK0 instead of literal PDG codes
1 parent b51d58c commit 6e4c8a6

3 files changed

Lines changed: 107 additions & 22 deletions

File tree

‎Common/Core/RecoDecay.h‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,30 @@ struct RecoDecay {
11791179
return OriginType::None;
11801180
}
11811181

1182+
/// Check whether the PDG code corresponds to a diquark state (e.g. 4101, 4201, 5101).
1183+
/// Diquarks are constituents used by the generators during the hadronisation and are not
1184+
/// hadrons, but their PDG codes satisfy the pdg / 1000 == 4 or 5 condition that is used
1185+
/// to identify charm and beauty baryons in getParticleOrigin.
1186+
/// \param pdgCode PDG code of the particle
1187+
/// \return true if the PDG code belongs to a diquark state
1188+
static bool isDiquark(const int pdgCode)
1189+
{
1190+
const int absPdgCode = std::abs(pdgCode);
1191+
// diquark codes are 4-digit numbers: two quark flavours followed by 01 (spin 0) or 03 (spin 1)
1192+
if (absPdgCode < 1101 || absPdgCode > 6603) {
1193+
return false;
1194+
}
1195+
const int spinCode = absPdgCode % 100;
1196+
if (spinCode != 1 && spinCode != 3) {
1197+
return false;
1198+
}
1199+
const int quark1 = (absPdgCode / 1000) % 10;
1200+
const int quark2 = (absPdgCode / 100) % 10;
1201+
return quark1 >= PDG_t::kDown && quark1 <= PdgQuarkMax &&
1202+
quark2 >= PDG_t::kDown && quark2 <= PdgQuarkMax &&
1203+
quark1 >= quark2;
1204+
}
1205+
11821206
/// based on getCharmHardronOrigin in order to extend general particle
11831207
/// Finding the origin (from charm hadronisation or beauty-hadron decay) of paritcle (b, c and others)
11841208
/// \param particlesMC table with MC particles
@@ -1243,6 +1267,7 @@ struct RecoDecay {
12431267
}
12441268
} else {
12451269
if (
1270+
!isDiquark(pdgParticleIMother) &&
12461271
(pdgParticleIMother / PdgDivisorMeson == PDG_t::kBottom || // b mesons
12471272
pdgParticleIMother / PdgDivisorBaryon == PDG_t::kBottom) // b baryons
12481273
) {
@@ -1252,6 +1277,7 @@ struct RecoDecay {
12521277
return OriginType::NonPrompt; // beauty
12531278
}
12541279
if (
1280+
!isDiquark(pdgParticleIMother) &&
12551281
(pdgParticleIMother / PdgDivisorMeson == PDG_t::kCharm || // c mesons
12561282
pdgParticleIMother / PdgDivisorBaryon == PDG_t::kCharm) // c baryons
12571283
) {

‎PWGCF/DataModel/FemtoDerived.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,7 @@ enum ParticleOriginMCTruth {
750750
kElse, //! none of the above; (NOTE: used to catch bugs. will be removed once MC usage is properly validated)
751751
kSecondaryFromHF, //! Kaon from a charm- or beauty-hadron decay chain
752752
kSecondaryFromLF, //! Kaon from a light-flavour decay without a heavy-flavour ancestor
753+
kSecondaryFromPhi, //! Kaon from a phi decay chain (phi -> K0/K0bar -> K0S)
753754
kNOriginMCTruthTypes
754755
};
755756

@@ -771,7 +772,8 @@ static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes
771772
"_SecondaryDaughterXistarMinus",
772773
"_Else",
773774
"_SecondaryFromHF",
774-
"_SecondaryFromLF"};
775+
"_SecondaryFromLF",
776+
"_SecondaryFromPhi"};
775777

776778
/// Distinguished between reconstructed and truth
777779
enum MCType {

‎PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx‎

Lines changed: 78 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"
@@ -494,8 +495,46 @@ struct HfProducerCharmHadronsV0FemtoDream {
494495
}
495496
}
496497

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

614671
template <bool IsMc = false, typename V0Type, typename CollisionType, typename TrackType>
615-
bool fillV0sForCharmHadron(CollisionType const& col, V0Type const& fullV0s, TrackType const& /*tracks*/)
672+
bool fillV0sForCharmHadron(CollisionType const& col, V0Type const& fullV0s, TrackType const& /*tracks*/, aod::McParticles const* mcParticles = nullptr)
616673
{
617674
const bool isK0S = (selectionFlagV0 == V0Channel::K0S);
618675
const bool isLambda = (selectionFlagV0 == V0Channel::Lambda);
@@ -724,15 +781,15 @@ struct HfProducerCharmHadronsV0FemtoDream {
724781
}
725782

726783
if constexpr (IsMc) {
727-
fillMcParticle(col, v0, o2::aod::femtodreamparticle::ParticleType::kV0);
784+
fillMcParticle(col, v0, o2::aod::femtodreamparticle::ParticleType::kV0, *mcParticles);
728785
}
729786
fIsV0Filled = true;
730787
}
731788
return fIsV0Filled;
732789
}
733790

734791
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)
792+
void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, V0Type const& fullV0s, CandType const& candidates, aod::McParticles const* mcParticles = nullptr)
736793
{
737794
const auto vtxZ = col.posZ();
738795
const auto sizeCand = candidates.size();
@@ -1019,7 +1076,7 @@ struct HfProducerCharmHadronsV0FemtoDream {
10191076
fillTable(2, candidate.isSelDstarToD0Pi(), outputMlDstar.at(0), outputMlDstar.at(1), outputMlDstar.at(2));
10201077
}
10211078
}
1022-
isV0Filled = fillV0sForCharmHadron<IsMc>(col, fullV0s, tracks);
1079+
isV0Filled = fillV0sForCharmHadron<IsMc>(col, fullV0s, tracks, mcParticles);
10231080
aod::femtodreamcollision::BitMaskType bitV0 = 0;
10241081
if (isV0Filled) {
10251082
bitV0 |= 1 << 0;
@@ -1248,26 +1305,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
12481305
aod::BCsWithTimestamps const&,
12491306
FemtoHFMcTracks const& tracks,
12501307
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1251-
aod::McParticles const&,
1308+
aod::McParticles const& mcParticles,
12521309
CandidateD0Mc const& candidates)
12531310
{
12541311
// get magnetic field for run
12551312
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1256-
fillCharmHadronTable<DecayChannel::D0ToPiK, true, false>(col, tracks, fullV0s, candidates);
1313+
fillCharmHadronTable<DecayChannel::D0ToPiK, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
12571314
}
12581315
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcD0ToPiK, "Provide Mc for D0ToPiK", false);
12591316

12601317
void processMcD0ToPiKWithML(FemtoFullCollisionMc const& col,
12611318
aod::BCsWithTimestamps const&,
12621319
FemtoHFMcTracks const& tracks,
12631320
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1264-
aod::McParticles const&,
1321+
aod::McParticles const& mcParticles,
12651322
soa::Join<CandidateD0Mc,
12661323
aod::HfMlD0> const& candidates)
12671324
{
12681325
// get magnetic field for run
12691326
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1270-
fillCharmHadronTable<DecayChannel::D0ToPiK, true, true>(col, tracks, fullV0s, candidates);
1327+
fillCharmHadronTable<DecayChannel::D0ToPiK, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
12711328
}
12721329
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcD0ToPiKWithML, "Provide Mc for D0ToPiK with ml", false);
12731330

@@ -1307,26 +1364,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
13071364
aod::BCsWithTimestamps const&,
13081365
FemtoHFMcTracks const& tracks,
13091366
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1310-
aod::McParticles const&,
1367+
aod::McParticles const& mcParticles,
13111368
CandidateDstarMc const& candidates)
13121369
{
13131370
// get magnetic field for run
13141371
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1315-
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, false>(col, tracks, fullV0s, candidates);
1372+
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
13161373
}
13171374
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDstarToD0Pi, "Provide Mc for DstarToD0Pi", false);
13181375

13191376
void processMcDstarToD0PiWithML(FemtoFullCollisionMc const& col,
13201377
aod::BCsWithTimestamps const&,
13211378
FemtoHFMcTracks const& tracks,
13221379
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1323-
aod::McParticles const&,
1380+
aod::McParticles const& mcParticles,
13241381
soa::Join<CandidateDstarMc,
13251382
aod::HfMlDstarToD0Pi> const& candidates)
13261383
{
13271384
// get magnetic field for run
13281385
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1329-
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, true>(col, tracks, fullV0s, candidates);
1386+
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
13301387
}
13311388
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDstarToD0PiWithML, "Provide Mc for DstarToD0Pi with ml", false);
13321389

@@ -1368,26 +1425,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
13681425
aod::BCsWithTimestamps const&,
13691426
FemtoHFMcTracks const& tracks,
13701427
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1371-
aod::McParticles const&,
1428+
aod::McParticles const& mcParticles,
13721429
CandidateDplusMc const& candidates)
13731430
{
13741431
// get magnetic field for run
13751432
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1376-
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, false>(col, tracks, fullV0s, candidates);
1433+
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
13771434
}
13781435
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDplusToPiKPi, "Provide Mc for DplusToPiKPi", false);
13791436

13801437
void processMcDplusToPiKPiWithML(FemtoFullCollisionMc const& col,
13811438
aod::BCsWithTimestamps const&,
13821439
FemtoHFMcTracks const& tracks,
13831440
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1384-
aod::McParticles const&,
1441+
aod::McParticles const& mcParticles,
13851442
soa::Join<CandidateDplusMc,
13861443
aod::HfMlDplusToPiKPi> const& candidates)
13871444
{
13881445
// get magnetic field for run
13891446
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1390-
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, true>(col, tracks, fullV0s, candidates);
1447+
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
13911448
}
13921449
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDplusToPiKPiWithML, "Provide Mc for DplusToPiKPi with ml", false);
13931450

@@ -1429,26 +1486,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
14291486
aod::BCsWithTimestamps const&,
14301487
FemtoHFMcTracks const& tracks,
14311488
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1432-
aod::McParticles const&,
1489+
aod::McParticles const& mcParticles,
14331490
CandidateLcMc const& candidates)
14341491
{
14351492
// get magnetic field for run
14361493
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1437-
fillCharmHadronTable<DecayChannel::LcToPKPi, true, false>(col, tracks, fullV0s, candidates);
1494+
fillCharmHadronTable<DecayChannel::LcToPKPi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
14381495
}
14391496
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcLcToPKPi, "Provide Mc for lctopkpi", false);
14401497

14411498
void processMcLcToPKPiWithML(FemtoFullCollisionMc const& col,
14421499
aod::BCsWithTimestamps const&,
14431500
FemtoHFMcTracks const& tracks,
14441501
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1445-
aod::McParticles const&,
1502+
aod::McParticles const& mcParticles,
14461503
soa::Join<CandidateLcMc,
14471504
aod::HfMlLcToPKPi> const& candidates)
14481505
{
14491506
// get magnetic field for run
14501507
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1451-
fillCharmHadronTable<DecayChannel::LcToPKPi, true, true>(col, tracks, fullV0s, candidates);
1508+
fillCharmHadronTable<DecayChannel::LcToPKPi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
14521509
}
14531510
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcLcToPKPiWithML, "Provide Mc for lctopkpi with ml", false);
14541511

0 commit comments

Comments
 (0)