Skip to content

Commit 557a34d

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) in the hadron-level mode 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; in the parton-level mode (searchUpToQuark) the walk still goes through diquarks, since a charm or beauty quark can be found above them - add RecoDecay::isDiquark() and PdgDiquarkSpin0Code/PdgDiquarkSpin1Code, built from the existing PDG constants and divisors (no literal PDG codes) - use o2::constants::physics::Pdg::kPhi and kK0 instead of literal PDG codes
1 parent b51d58c commit 557a34d

3 files changed

Lines changed: 117 additions & 22 deletions

File tree

‎Common/Core/RecoDecay.h‎

Lines changed: 34 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,27 @@ 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, but their PDG codes satisfy the pdg / 1000 == 4 or 5 condition that is used
1187+
/// to identify charm and beauty baryons in getParticleOrigin.
1188+
/// \param pdgCode PDG code of the particle
1189+
/// \return true if the PDG code belongs to a diquark state
1190+
static bool isDiquark(const int pdgCode)
1191+
{
1192+
// a diquark PDG code is composed as quark1 * PdgDivisorBaryon + quark2 * PdgDivisorMeson +
1193+
// spinCode, with quark1 >= quark2 the quark flavours and spinCode either PdgDiquarkSpin0Code
1194+
// or PdgDiquarkSpin1Code
1195+
const int absPdgCode = std::abs(pdgCode);
1196+
const int spinCode = absPdgCode % PdgDivisorMeson;
1197+
if (spinCode != PdgDiquarkSpin0Code && spinCode != PdgDiquarkSpin1Code) {
1198+
return false;
1199+
}
1200+
const int quark1 = absPdgCode / PdgDivisorBaryon;
1201+
const int quark2 = (absPdgCode % PdgDivisorBaryon) / PdgDivisorMeson;
1202+
return quark1 >= PDG_t::kDown && quark1 <= PDG_t::kTop && quark2 >= PDG_t::kDown && quark1 >= quark2;
1203+
}
1204+
11821205
/// based on getCharmHardronOrigin in order to extend general particle
11831206
/// Finding the origin (from charm hadronisation or beauty-hadron decay) of paritcle (b, c and others)
11841207
/// \param particlesMC table with MC particles
@@ -1221,6 +1244,17 @@ struct RecoDecay {
12211244
// Check status code
12221245
// auto motherStatusCode = std::abs(mother.getGenStatusCode());
12231246
auto pdgParticleIMother = std::abs(mother.pdgCode()); // PDG code of the mother
1247+
// in the hadron-level mode, stop the walk when a parton or a diquark is reached: no
1248+
// heavy-flavour hadron can be found above the partonic level, and a diquark is not a
1249+
// hadron although its PDG code (e.g. 4101, 4201, 5101) satisfies pdg / 1000 == 4 or 5
1250+
// and would be misidentified as a charm/beauty baryon below. In the parton-level mode
1251+
// the walk has to continue through diquarks, since a charm or beauty quark can still be
1252+
// found above them.
1253+
if (!searchUpToQuark &&
1254+
(isDiquark(pdgParticleIMother) || pdgParticleIMother <= PdgQuarkMax ||
1255+
(pdgParticleIMother >= PdgBosonMin && pdgParticleIMother <= PdgBosonMax))) {
1256+
break;
1257+
}
12241258
// Check mother's PDG code.
12251259
// printf("getMother: ");
12261260
// 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: 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 int 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)