Skip to content

Commit ab9dd92

Browse files
committed
Add additional function to distinguish the other hadron cases for PCM, EMC and PCMEMC
1 parent 6b61522 commit ab9dd92

1 file changed

Lines changed: 160 additions & 0 deletions

File tree

‎PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx‎

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include <TH2.h>
5151
#include <TH3.h>
5252
#include <THn.h>
53+
#include <TMCProcess.h>
5354
#include <TPDGCode.h>
5455

5556
#include <algorithm>
@@ -59,6 +60,7 @@
5960
#include <cstdint>
6061
#include <cstdlib>
6162
#include <format>
63+
#include <iterator>
6264
#include <optional>
6365
#include <string>
6466
#include <string_view>
@@ -119,6 +121,36 @@ enum class LegMatchStatus : uint8_t {
119121
kNStatus
120122
};
121123

124+
/// \brief Species of the closest common ancestor of an "other hadron" pair
125+
enum class OtherHadronSpecies : uint8_t {
126+
kChargedPion = 0,
127+
kChargedKaon,
128+
kK0L,
129+
kRho,
130+
kKstar,
131+
kPhi,
132+
kOtherMeson, // incl. f0, f2, a, b, h resonances
133+
kProton,
134+
kNeutron,
135+
kDelta,
136+
kLambda,
137+
kSigma,
138+
kXi,
139+
kOtherBaryon,
140+
kOther,
141+
kNSpecies
142+
};
143+
144+
/// \brief How the particles directly below the common ancestor were produced, i.e. how the ancestor split into the two branches.
145+
/// Ordered such that std::max of the two branches gives the material interaction if one branch comes from it.
146+
enum class SplitProduction : uint8_t {
147+
kGeneratorDecay = 0, // produced by the event generator: decay in the generator (resonances, Sigma0, ...)
148+
kTransportDecay, // produced by the transport code in a decay (weak decays of K+-, K0L, Lambda, Xi, ...)
149+
kHadronicInteraction, // produced by the transport code in a hadronic interaction in the detector material
150+
kOtherProcess, // produced by the transport code in any other process
151+
kNProduction
152+
};
153+
122154
enum CentralityEstimator {
123155
None = 0,
124156
CFT0A = 1,
@@ -388,6 +420,25 @@ struct TwoPhotonMcTask {
388420
for (std::size_t i = 0; i < BucketLabels.size(); ++i) {
389421
hPairAncestor->GetAxis(4)->SetBinLabel(static_cast<int>(i) + 1, BucketLabels[i].data());
390422
}
423+
// "other hadron" pairs: which hadron is the common ancestor, and did it split in a decay or a material interaction
424+
const AxisSpec axisOHMass{160, 0., 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"};
425+
const AxisSpec axisOHPt{20, 0., 20., "#it{p}_{T} (GeV/#it{c})"};
426+
const AxisSpec axisOHSpecies{static_cast<int>(OtherHadronSpecies::kNSpecies), -0.5, static_cast<int>(OtherHadronSpecies::kNSpecies) - 0.5, "common ancestor"};
427+
const AxisSpec axisOHProduction{static_cast<int>(SplitProduction::kNProduction), -0.5, static_cast<int>(SplitProduction::kNProduction) - 0.5, "split of the common ancestor"};
428+
auto hOtherHadron = registry.add<THn>("PCM/OtherHadron/hAncestor", "other hadron pairs: minv vs pT vs ancestor species vs split production vs pair truth", HistType::kTHnF, {axisOHMass, axisOHPt, axisOHSpecies, axisOHProduction, thnAxisPairTruth});
429+
static constexpr std::array<std::string_view, static_cast<int>(OtherHadronSpecies::kNSpecies)> SpeciesLabels = {"#pi^{#pm}", "K^{#pm}", "K^{0}_{L}", "#rho", "K^{*}", "#phi", "other meson", "p", "n", "#Delta", "#Lambda", "#Sigma", "#Xi", "other baryon", "other"};
430+
for (std::size_t i = 0; i < SpeciesLabels.size(); ++i) {
431+
hOtherHadron->GetAxis(2)->SetBinLabel(static_cast<int>(i) + 1, SpeciesLabels[i].data());
432+
}
433+
static constexpr std::array<std::string_view, static_cast<int>(SplitProduction::kNProduction)> ProductionLabels = {"generator decay", "transport decay", "hadronic interaction", "other process"};
434+
for (std::size_t i = 0; i < ProductionLabels.size(); ++i) {
435+
hOtherHadron->GetAxis(3)->SetBinLabel(static_cast<int>(i) + 1, ProductionLabels[i].data());
436+
}
437+
for (std::size_t i = 0; i < TruthLabels.size(); ++i) {
438+
hOtherHadron->GetAxis(4)->SetBinLabel(static_cast<int>(i) + 1, TruthLabels[i].data());
439+
}
440+
registry.add("PCM/OtherHadron/hAncestorPdg", "other hadron pairs: |PDG| of the common ancestor;|PDG| (last bin: #geq 9999);pairs", HistType::kTH1D, {{10000, -0.5, 9999.5}});
441+
391442
// mixed event, same axes as hPairAncestor without the bucket (pairs from different events share no ancestor)
392443
auto hMixed = registry.add<THn>("PCM/hMixed", "minv vs pT vs cent vs pair truth, mixed event", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth});
393444
for (std::size_t i = 0; i < TruthLabels.size(); ++i) {
@@ -808,6 +859,112 @@ struct TwoPhotonMcTask {
808859
fillLegMatch<true>(matchedSeconds.sliceBy(perEMCClusterMS, emc.globalIndex()), emc.e(), pLeg, ptLeg);
809860
}
810861

862+
/// \brief Species class of the common ancestor of an "other hadron" pair
863+
/// \param pdg PDG code of the ancestor
864+
static OtherHadronSpecies otherHadronSpecies(const int pdg)
865+
{
866+
const int absPdg = std::abs(pdg);
867+
switch (absPdg) {
868+
case PDG_t::kPiPlus:
869+
return OtherHadronSpecies::kChargedPion;
870+
case PDG_t::kKPlus:
871+
return OtherHadronSpecies::kChargedKaon;
872+
case PDG_t::kK0Long:
873+
return OtherHadronSpecies::kK0L;
874+
case PDG_t::kRho770_0:
875+
case PDG_t::kRho770Plus:
876+
return OtherHadronSpecies::kRho;
877+
case o2::constants::physics::Pdg::kK0Star892:
878+
case o2::constants::physics::Pdg::kKPlusStar892:
879+
return OtherHadronSpecies::kKstar;
880+
case o2::constants::physics::Pdg::kPhi:
881+
return OtherHadronSpecies::kPhi;
882+
case PDG_t::kProton:
883+
return OtherHadronSpecies::kProton;
884+
case PDG_t::kNeutron:
885+
return OtherHadronSpecies::kNeutron;
886+
case 1114: // o2-linter: disable=pdg/explicit-code (Delta-, no named constant)
887+
case 2114: // o2-linter: disable=pdg/explicit-code (Delta0, no named constant)
888+
case 2214: // o2-linter: disable=pdg/explicit-code (Delta+, no named constant)
889+
case 2224: // o2-linter: disable=pdg/explicit-code (Delta++, no named constant)
890+
return OtherHadronSpecies::kDelta;
891+
case PDG_t::kLambda0:
892+
return OtherHadronSpecies::kLambda;
893+
case PDG_t::kSigmaMinus:
894+
case PDG_t::kSigma0:
895+
case PDG_t::kSigmaPlus:
896+
return OtherHadronSpecies::kSigma;
897+
case PDG_t::kXiMinus:
898+
case 3322: // o2-linter: disable=pdg/explicit-code (Xi0, no named constant)
899+
return OtherHadronSpecies::kXi;
900+
default:
901+
break;
902+
}
903+
constexpr int MinHadronPdg = 100;
904+
constexpr int MaxHadronPdg = 10000000;
905+
if (absPdg < MinHadronPdg || absPdg >= MaxHadronPdg) {
906+
return OtherHadronSpecies::kOther;
907+
}
908+
// PDG numbering: mesons have no third quark, i.e. the thousands digit is 0 (also for 9000000 type resonances)
909+
return (absPdg / 1000) % 10 == 0 ? OtherHadronSpecies::kOtherMeson : OtherHadronSpecies::kOtherBaryon; // o2-linter: disable=magic-number (PDG digit)
910+
}
911+
912+
/// \brief How an MC particle was produced
913+
/// \param mcIter iterator of the full MC particle table, WILL BE CHANGED
914+
/// \param id index of the particle
915+
template <o2::soa::is_iterator TMCIter>
916+
static SplitProduction productionOf(TMCIter& mcIter, const int id)
917+
{
918+
mcIter.setCursor(id);
919+
if (mcIter.producedByGenerator()) {
920+
return SplitProduction::kGeneratorDecay;
921+
}
922+
switch (mcIter.getProcess()) {
923+
case TMCProcess::kPDecay:
924+
return SplitProduction::kTransportDecay;
925+
case TMCProcess::kPHadronic:
926+
case TMCProcess::kPHInhelastic:
927+
case TMCProcess::kPNuclearAbsorption:
928+
case TMCProcess::kPNCapture:
929+
case TMCProcess::kPPbarAnnihilation:
930+
case TMCProcess::kPNbarAnnihilation:
931+
case TMCProcess::kPEvaporation:
932+
case TMCProcess::kPNuclearFission:
933+
case TMCProcess::kPPhotoNuclear:
934+
case TMCProcess::kPPhotonInhelastic:
935+
return SplitProduction::kHadronicInteraction;
936+
default:
937+
return SplitProduction::kOtherProcess;
938+
}
939+
}
940+
941+
/// \brief Fill which hadron is the common ancestor of an "other hadron" pair and how it split into the two branches
942+
/// \param chain1 ancestor chain of the first candidate
943+
/// \param chain2 ancestor chain of the second candidate
944+
/// \param pairTruth truth flag of the pair
945+
/// \param vMeson pair four vector
946+
/// \param mcIter iterator of the full MC particle table, WILL BE CHANGED
947+
template <int pairType, o2::soa::is_iterator TMCIter>
948+
void diagnoseOtherHadron(Chain const& chain1, Chain const& chain2, const PhotonTruth pairTruth, ROOT::Math::PtEtaPhiMVector const& vMeson, TMCIter& mcIter)
949+
{
950+
static constexpr std::array<std::string_view, 3> HistAncestor = {"PCM/OtherHadron/hAncestor", "EMC/OtherHadron/hAncestor", "PCMEMC/OtherHadron/hAncestor"};
951+
static constexpr std::array<std::string_view, 3> HistAncestorPdg = {"PCM/OtherHadron/hAncestorPdg", "EMC/OtherHadron/hAncestorPdg", "PCMEMC/OtherHadron/hAncestorPdg"};
952+
static constexpr int MaxPdgBin = 9999;
953+
954+
const int ancestor = getFirstCommonAncestor(chain1, chain2);
955+
const auto pos1 = std::distance(chain1.begin(), std::ranges::find(chain1, ancestor));
956+
const auto pos2 = std::distance(chain2.begin(), std::ranges::find(chain2, ancestor));
957+
if (ancestor < 0 || pos1 < 1 || pos2 < 1 || pos1 >= std::ssize(chain1) || pos2 >= std::ssize(chain2)) {
958+
return; // not possible for "other hadron" (the ancestor is neither of the candidates), protection only
959+
}
960+
// the particle directly below the ancestor in each branch tells how the ancestor split
961+
const SplitProduction production = std::max(productionOf(mcIter, chain1[pos1 - 1]), productionOf(mcIter, chain2[pos2 - 1]));
962+
mcIter.setCursor(ancestor);
963+
const int pdg = mcIter.pdgCode();
964+
registry.fill(HIST(HistAncestor[pairType]), vMeson.M(), vMeson.Pt(), static_cast<int>(otherHadronSpecies(pdg)), static_cast<int>(production), static_cast<int>(pairTruth));
965+
registry.fill(HIST(HistAncestorPdg[pairType]), std::min(std::abs(pdg), MaxPdgBin));
966+
}
967+
811968
/// \brief Apply the pair cuts and fill the pair ancestor histogram
812969
/// \return bucket of the pair, std::nullopt if the pair was rejected
813970
template <int pairType, o2::soa::is_iterator TPhoton1, o2::soa::is_iterator TPhoton2, o2::soa::is_iterator TMCIter>
@@ -830,6 +987,9 @@ struct TwoPhotonMcTask {
830987
const PhotonTruth pairTruth = std::max(truth1, truth2);
831988
const PairBucket bucket = classifyPair(chain1, chain2, mcIter);
832989
registry.fill(HIST(HistPairAncestor[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast<int>(pairTruth), static_cast<int>(bucket));
990+
if (bucket == PairBucket::kCrossOtherHadron) {
991+
diagnoseOtherHadron<pairType>(chain1, chain2, pairTruth, vMeson, mcIter);
992+
}
833993
if (mesonConfig.cfgEnableQA.value) {
834994
registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt());
835995
}

0 commit comments

Comments
 (0)