Skip to content

Commit 251115b

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, kSecondaryFromLF and kSecondaryFromPhi - 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 - RecoDecay::getParticleOrigin now stops the mother walk (break) as soon as a parton or a diquark is reached: no heavy-flavour hadron can be found above the partonic level, and diquark PDG codes (4101, 4201, 5101, ...) satisfy pdg / 1000 == 4 or 5 and were otherwise misidentified as charm/beauty baryons (RecoDecay::isDiquark is added for that); the parton stop follows the !searchUpToQuark idiom of getMother() so that the search-up-to-quark mode still reaches the charm and beauty quarks - use o2::constants::physics::Pdg::kPhi and kK0 instead of literal PDG codes
1 parent b51d58c commit 251115b

3 files changed

Lines changed: 114 additions & 22 deletions

File tree

‎Common/Core/RecoDecay.h‎

Lines changed: 32 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
@@ -1221,6 +1245,14 @@ struct RecoDecay {
12211245
// Check status code
12221246
// auto motherStatusCode = std::abs(mother.getGenStatusCode());
12231247
auto pdgParticleIMother = std::abs(mother.pdgCode()); // PDG code of the mother
1248+
// stop the walk when a parton or a diquark is reached: no heavy-flavour hadron can be
1249+
// found above the partonic level, and a diquark is not a hadron although its PDG code
1250+
// (e.g. 4101, 4201, 5101) satisfies pdg / 1000 == 4 or 5 and would be misidentified as
1251+
// a charm/beauty baryon below
1252+
if (isDiquark(pdgParticleIMother) ||
1253+
(!searchUpToQuark && (pdgParticleIMother <= PdgQuarkMax || (pdgParticleIMother >= PdgBosonMin && pdgParticleIMother <= PdgBosonMax)))) {
1254+
break;
1255+
}
12241256
// Check mother's PDG code.
12251257
// printf("getMother: ");
12261258
// for (int i = stage; i < 0; i++) // Indent to make the tree look nice.

‎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: 79 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,47 @@ struct HfProducerCharmHadronsV0FemtoDream {
494495
}
495496
}
496497

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

614672
template <bool IsMc = false, typename V0Type, typename CollisionType, typename TrackType>
615-
bool fillV0sForCharmHadron(CollisionType const& col, V0Type const& fullV0s, TrackType const& /*tracks*/)
673+
bool fillV0sForCharmHadron(CollisionType const& col, V0Type const& fullV0s, TrackType const& /*tracks*/, aod::McParticles const* mcParticles = nullptr)
616674
{
617675
const bool isK0S = (selectionFlagV0 == V0Channel::K0S);
618676
const bool isLambda = (selectionFlagV0 == V0Channel::Lambda);
@@ -724,15 +782,15 @@ struct HfProducerCharmHadronsV0FemtoDream {
724782
}
725783

726784
if constexpr (IsMc) {
727-
fillMcParticle(col, v0, o2::aod::femtodreamparticle::ParticleType::kV0);
785+
fillMcParticle(col, v0, o2::aod::femtodreamparticle::ParticleType::kV0, *mcParticles);
728786
}
729787
fIsV0Filled = true;
730788
}
731789
return fIsV0Filled;
732790
}
733791

734792
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)
793+
void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, V0Type const& fullV0s, CandType const& candidates, aod::McParticles const* mcParticles = nullptr)
736794
{
737795
const auto vtxZ = col.posZ();
738796
const auto sizeCand = candidates.size();
@@ -1019,7 +1077,7 @@ struct HfProducerCharmHadronsV0FemtoDream {
10191077
fillTable(2, candidate.isSelDstarToD0Pi(), outputMlDstar.at(0), outputMlDstar.at(1), outputMlDstar.at(2));
10201078
}
10211079
}
1022-
isV0Filled = fillV0sForCharmHadron<IsMc>(col, fullV0s, tracks);
1080+
isV0Filled = fillV0sForCharmHadron<IsMc>(col, fullV0s, tracks, mcParticles);
10231081
aod::femtodreamcollision::BitMaskType bitV0 = 0;
10241082
if (isV0Filled) {
10251083
bitV0 |= 1 << 0;
@@ -1248,26 +1306,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
12481306
aod::BCsWithTimestamps const&,
12491307
FemtoHFMcTracks const& tracks,
12501308
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1251-
aod::McParticles const&,
1309+
aod::McParticles const& mcParticles,
12521310
CandidateD0Mc const& candidates)
12531311
{
12541312
// get magnetic field for run
12551313
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1256-
fillCharmHadronTable<DecayChannel::D0ToPiK, true, false>(col, tracks, fullV0s, candidates);
1314+
fillCharmHadronTable<DecayChannel::D0ToPiK, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
12571315
}
12581316
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcD0ToPiK, "Provide Mc for D0ToPiK", false);
12591317

12601318
void processMcD0ToPiKWithML(FemtoFullCollisionMc const& col,
12611319
aod::BCsWithTimestamps const&,
12621320
FemtoHFMcTracks const& tracks,
12631321
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1264-
aod::McParticles const&,
1322+
aod::McParticles const& mcParticles,
12651323
soa::Join<CandidateD0Mc,
12661324
aod::HfMlD0> const& candidates)
12671325
{
12681326
// get magnetic field for run
12691327
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1270-
fillCharmHadronTable<DecayChannel::D0ToPiK, true, true>(col, tracks, fullV0s, candidates);
1328+
fillCharmHadronTable<DecayChannel::D0ToPiK, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
12711329
}
12721330
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcD0ToPiKWithML, "Provide Mc for D0ToPiK with ml", false);
12731331

@@ -1307,26 +1365,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
13071365
aod::BCsWithTimestamps const&,
13081366
FemtoHFMcTracks const& tracks,
13091367
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1310-
aod::McParticles const&,
1368+
aod::McParticles const& mcParticles,
13111369
CandidateDstarMc const& candidates)
13121370
{
13131371
// get magnetic field for run
13141372
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1315-
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, false>(col, tracks, fullV0s, candidates);
1373+
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
13161374
}
13171375
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDstarToD0Pi, "Provide Mc for DstarToD0Pi", false);
13181376

13191377
void processMcDstarToD0PiWithML(FemtoFullCollisionMc const& col,
13201378
aod::BCsWithTimestamps const&,
13211379
FemtoHFMcTracks const& tracks,
13221380
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1323-
aod::McParticles const&,
1381+
aod::McParticles const& mcParticles,
13241382
soa::Join<CandidateDstarMc,
13251383
aod::HfMlDstarToD0Pi> const& candidates)
13261384
{
13271385
// get magnetic field for run
13281386
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1329-
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, true>(col, tracks, fullV0s, candidates);
1387+
fillCharmHadronTable<DecayChannel::DstarToD0Pi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
13301388
}
13311389
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDstarToD0PiWithML, "Provide Mc for DstarToD0Pi with ml", false);
13321390

@@ -1368,26 +1426,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
13681426
aod::BCsWithTimestamps const&,
13691427
FemtoHFMcTracks const& tracks,
13701428
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1371-
aod::McParticles const&,
1429+
aod::McParticles const& mcParticles,
13721430
CandidateDplusMc const& candidates)
13731431
{
13741432
// get magnetic field for run
13751433
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1376-
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, false>(col, tracks, fullV0s, candidates);
1434+
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
13771435
}
13781436
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDplusToPiKPi, "Provide Mc for DplusToPiKPi", false);
13791437

13801438
void processMcDplusToPiKPiWithML(FemtoFullCollisionMc const& col,
13811439
aod::BCsWithTimestamps const&,
13821440
FemtoHFMcTracks const& tracks,
13831441
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1384-
aod::McParticles const&,
1442+
aod::McParticles const& mcParticles,
13851443
soa::Join<CandidateDplusMc,
13861444
aod::HfMlDplusToPiKPi> const& candidates)
13871445
{
13881446
// get magnetic field for run
13891447
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1390-
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, true>(col, tracks, fullV0s, candidates);
1448+
fillCharmHadronTable<DecayChannel::DplusToPiKPi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
13911449
}
13921450
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcDplusToPiKPiWithML, "Provide Mc for DplusToPiKPi with ml", false);
13931451

@@ -1429,26 +1487,26 @@ struct HfProducerCharmHadronsV0FemtoDream {
14291487
aod::BCsWithTimestamps const&,
14301488
FemtoHFMcTracks const& tracks,
14311489
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1432-
aod::McParticles const&,
1490+
aod::McParticles const& mcParticles,
14331491
CandidateLcMc const& candidates)
14341492
{
14351493
// get magnetic field for run
14361494
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1437-
fillCharmHadronTable<DecayChannel::LcToPKPi, true, false>(col, tracks, fullV0s, candidates);
1495+
fillCharmHadronTable<DecayChannel::LcToPKPi, true, false>(col, tracks, fullV0s, candidates, &mcParticles);
14381496
}
14391497
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcLcToPKPi, "Provide Mc for lctopkpi", false);
14401498

14411499
void processMcLcToPKPiWithML(FemtoFullCollisionMc const& col,
14421500
aod::BCsWithTimestamps const&,
14431501
FemtoHFMcTracks const& tracks,
14441502
soa::Join<o2::aod::V0Datas, aod::McV0Labels> const& fullV0s,
1445-
aod::McParticles const&,
1503+
aod::McParticles const& mcParticles,
14461504
soa::Join<CandidateLcMc,
14471505
aod::HfMlLcToPKPi> const& candidates)
14481506
{
14491507
// get magnetic field for run
14501508
getMagneticFieldTesla(col.bc_as<aod::BCsWithTimestamps>());
1451-
fillCharmHadronTable<DecayChannel::LcToPKPi, true, true>(col, tracks, fullV0s, candidates);
1509+
fillCharmHadronTable<DecayChannel::LcToPKPi, true, true>(col, tracks, fullV0s, candidates, &mcParticles);
14521510
}
14531511
PROCESS_SWITCH(HfProducerCharmHadronsV0FemtoDream, processMcLcToPKPiWithML, "Provide Mc for lctopkpi with ml", false);
14541512

0 commit comments

Comments
 (0)