Skip to content

Commit 132b8b8

Browse files
author
Bong-Hwi Lim
committed
[PWGLF] Align K1 MC parent truth and efficiency response
1 parent 4f7fdfb commit 132b8b8

4 files changed

Lines changed: 101 additions & 22 deletions

File tree

‎PWGLF/Core/K1AnalysisMicroCore.h‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <limits>
4444
#include <set>
4545
#include <type_traits>
46+
#include <unordered_map>
4647
#include <utility>
4748
#include <vector>
4849

@@ -64,6 +65,16 @@ enum class K1TruthChannel {
6465
KStarPi = 2
6566
};
6667

68+
struct K1ParentTruth {
69+
double pt = std::numeric_limits<double>::quiet_NaN();
70+
double y = std::numeric_limits<double>::quiet_NaN();
71+
int pdgCode = 0;
72+
K1TruthChannel channel = K1TruthChannel::None;
73+
};
74+
using K1ParentTruthMap = std::unordered_map<int64_t, K1ParentTruth>;
75+
76+
inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2);
77+
6778
// The value is the species index of the PID configuration in ResoAnalysisSelectionCore.
6879
enum class Species : int {
6980
Pion = 0,
@@ -217,6 +228,49 @@ K1TruthChannel classifyK1Truth(const Track& pion1, const Track& pion2, const Kao
217228
return K1TruthChannel::None;
218229
}
219230

231+
template <typename Kaon, typename Pion>
232+
int k1MotherId(K1TruthChannel channel, const Kaon& kaon, const Pion& pion1, const Pion& pion2)
233+
{
234+
if (channel == K1TruthChannel::RhoK) {
235+
return kaon.motherId();
236+
}
237+
if (channel == K1TruthChannel::KStarPi) {
238+
return std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() : pion2.motherId();
239+
}
240+
return -1;
241+
}
242+
243+
template <typename ParentsType>
244+
K1ParentTruthMap makeK1ParentTruthMap(const ParentsType& parents)
245+
{
246+
K1ParentTruthMap result;
247+
for (const auto& part : parents) {
248+
if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus || part.originalMcParticleId() < 0) {
249+
continue;
250+
}
251+
const auto id = static_cast<int64_t>(part.originalMcParticleId());
252+
const int charge = part.pdgCode() > 0 ? 1 : -1;
253+
result.emplace(id, K1ParentTruth{part.pt(), part.y(), part.pdgCode(), classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2())});
254+
}
255+
return result;
256+
}
257+
258+
inline const K1ParentTruth* findK1ParentTruth(const K1ParentTruthMap& parents, int64_t motherId,
259+
K1TruthChannel channel, int charge)
260+
{
261+
const auto it = parents.find(motherId);
262+
if (it == parents.end()) {
263+
return nullptr;
264+
}
265+
const auto& parent = it->second;
266+
const int expectedPdg = charge * o2::constants::physics::Pdg::kK1_1270Plus;
267+
if (channel == K1TruthChannel::None || parent.channel != channel || parent.pdgCode != expectedPdg ||
268+
!std::isfinite(parent.pt) || parent.pt < 0. || !std::isfinite(parent.y)) {
269+
return nullptr;
270+
}
271+
return &parent;
272+
}
273+
220274
// Immediate decay channel of a generated K1 from the PDG codes of its two daughters.
221275
inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2)
222276
{
@@ -635,7 +689,8 @@ class K1AnalysisMicroCore
635689
const int charge = part.pdgCode() > 0 ? 1 : -1;
636690
const K1TruthChannel channel = classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2());
637691
histos.fill(HIST("CutFlow/generated"), 0, static_cast<int>(channel));
638-
if (part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) {
692+
if (!std::isfinite(part.pt()) || part.pt() < 0. || !std::isfinite(part.y()) ||
693+
part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) {
639694
continue;
640695
}
641696
histos.fill(HIST("CutFlow/generated"), 1, static_cast<int>(channel));

‎PWGLF/DataModel/LFK1MlTables.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ DECLARE_SOA_COLUMN(K1TruthStatus, k1TruthStatus, uint8_t); //! 0 data,
6969
DECLARE_SOA_COLUMN(K1TruthChannel, k1TruthChannel, uint8_t); //! 0 none, 1 rho K, 2 K* pi
7070
DECLARE_SOA_COLUMN(K1MotherPdg, k1MotherPdg, int32_t);
7171
DECLARE_SOA_COLUMN(K1MotherId, k1MotherId, int64_t);
72-
DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! NaN in K1MlTruth: not filled for reconstructed candidates
73-
DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! NaN in K1MlTruth: not filled for reconstructed candidates
72+
DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! Parent-table pT for a validated reconstructed MC match; NaN otherwise
73+
DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! Parent-table y for a validated reconstructed MC match; NaN otherwise
7474
DECLARE_SOA_COLUMN(K1OriginalMcParticleId, k1OriginalMcParticleId, int64_t);
7575
DECLARE_SOA_COLUMN(K1DaughterPdg1, k1DaughterPdg1, int32_t);
7676
DECLARE_SOA_COLUMN(K1DaughterPdg2, k1DaughterPdg2, int32_t);

‎PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx‎

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ struct HistogramOptions : ConfigurableGroup {
5252
Configurable<int> cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"};
5353
Configurable<bool> additionalQAplots{"additionalQAplots", true, "Additional QA plots"};
5454
Configurable<int> cfgTruthDebug{"cfgTruthDebug", 0, "Maximum logged matched candidates per truth channel"};
55+
Configurable<bool> cUseTruthRapidity{"cUseTruthRapidity", true, "Require generated K1 rapidity in the configured window for MC efficiency numerators"};
5556
};
5657

5758
enum BinAnti : unsigned int {
@@ -231,12 +232,15 @@ struct K1AnalysisMicro {
231232
AxisSpec channelAxis = {3, -0.5, 2.5, "0: non-K1, 1: rho K, 2: K* pi"};
232233
histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}});
233234
histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}});
234-
histos.add("MCReco/channel", "All selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis});
235+
// These channel and pT histograms are pre-truth-fiducial QA; MCReco/response is the validated numerator.
236+
histos.add("MCReco/channel", "Pre-truth-fiducial QA: selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis});
235237
histos.add("MCReco/mass", "Reconstructed mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisReso});
236-
histos.add("MCReco/pt", "Reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis});
238+
histos.add("MCReco/pt", "Pre-truth-fiducial QA: reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis});
237239
histos.add("MCReco/piPiMass", "pi-pi mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisRho});
238240
histos.add("MCReco/pi1KMass", "First pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892});
239241
histos.add("MCReco/pi2KMass", "Second pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892});
242+
histos.add("MCReco/response", "Selected MC candidates;reconstructed pT;generated pT;channel;charge;centrality", HistType::kTHnSparseD,
243+
{ptAxis, ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis});
240244
histos.add("k1invmass_MC", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso});
241245
histos.add("k1invmass_MC_noK1", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso});
242246

@@ -272,6 +276,8 @@ struct K1AnalysisMicro {
272276
AxisSpec channelAxis = {3, -0.5, 2.5, "0: other/unresolved, 1: rho K, 2: K* pi"};
273277
histos.add("MCGen/chargeChannel", "K1 parents in selected reconstructed events, inside the K1 rapidity window", HistType::kTH2D, {{2, -1.5, 1.5, "K1 charge"}, channelAxis});
274278
histos.add("MCGen/ptChannel", "Generated K1 pT by immediate decay channel", HistType::kTH2D, {channelAxis, ptAxis});
279+
histos.add("MCGen/response", "Generated K1 parents;generated pT;channel;charge;centrality", HistType::kTHnSparseD,
280+
{ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis});
275281
}
276282
}
277283

@@ -380,7 +386,8 @@ struct K1AnalysisMicro {
380386
// (or one mixed pair of collisions). The selection itself is the shared K1 core.
381387
// dTracks1: bachelor kaons, dTracks2: pions.
382388
template <bool IsMC, bool IsMix, bool IsResoMicrotrack, typename CollisionType, typename TracksType>
383-
void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2)
389+
void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2,
390+
const K1ParentTruthMap* parentTruth = nullptr)
384391
{
385392
const bool fillQA = !IsMix && histogramOptions.additionalQAplots;
386393
const auto multiplicity = collision.cent();
@@ -474,9 +481,17 @@ struct K1AnalysisMicro {
474481
pion1.motherPDG(), pion2.motherPDG(), kaon.motherPDG(),
475482
pion1.siblingIds()[0], pion1.siblingIds()[1], pion2.siblingIds()[0], pion2.siblingIds()[1], kaon.siblingIds()[0], kaon.siblingIds()[1]);
476483
}
477-
typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec;
478-
histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M());
479-
histos.fill(HIST("k1invmass_MC"), c.k1.M());
484+
const int motherId = k1MotherId(channel, kaon, pion1, pion2);
485+
const int charge = kaon.pdgCode() > 0 ? 1 : -1;
486+
const auto* parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr;
487+
const bool truthRapidity = c.isUnlikeSign && parent && (!histogramOptions.cUseTruthRapidity || (parent->y >= candidateCuts.cK1MinRap && parent->y <= candidateCuts.cK1MaxRap));
488+
if (truthRapidity) {
489+
typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec;
490+
histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M());
491+
histos.fill(HIST("k1invmass_MC"), c.k1.M());
492+
const int parentCharge = parent->pdgCode > 0 ? 1 : -1;
493+
histos.fill(HIST("MCReco/response"), c.k1.Pt(), parent->pt, channelBin, parentCharge, multiplicity);
494+
}
480495
histos.fill(HIST("QAMC/K1OA"), c.angle);
481496
histos.fill(HIST("QAMC/K1PairAsym"), c.pairAsym);
482497
histos.fill(HIST("QAMC/hInvmassK892_Rho"), c.mass13, c.secondary.M());
@@ -515,17 +530,18 @@ struct K1AnalysisMicro {
515530
PROCESS_SWITCH(K1AnalysisMicro, processResoMicroTracks, "Process ResoMicroTracks", true);
516531

517532
void processMC(ResoMCCols::iterator const& collision,
518-
ResoMCTracks const& resotracks)
533+
ResoMCTracks const& resotracks, ResoMCParents const& resoParents)
519534
{
520535
if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) {
521536
return;
522537
}
523538
histos.fill(HIST("MCReco/collisions"), 0.5);
524-
fillHistograms<true, false, false>(collision, resotracks, resotracks);
539+
const auto parentTruth = makeK1ParentTruthMap(resoParents);
540+
fillHistograms<true, false, false>(collision, resotracks, resotracks, &parentTruth);
525541
}
526542
PROCESS_SWITCH(K1AnalysisMicro, processMC, "Process Event for MC", false);
527543

528-
void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks)
544+
void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents)
529545
{
530546
// The modular producer already selected these reconstructed collisions.
531547
// Apply precisely the same reconstruction loop as the data baseline.
@@ -534,7 +550,8 @@ struct K1AnalysisMicro {
534550
}
535551
histos.fill(HIST("MCReco/collisions"), 0.5);
536552
histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size());
537-
fillHistograms<true, false, true>(collision, tracks, tracks);
553+
const auto parentTruth = makeK1ParentTruthMap(resoParents);
554+
fillHistograms<true, false, true>(collision, tracks, tracks, &parentTruth);
538555
}
539556
PROCESS_SWITCH(K1AnalysisMicro, processMCMicro, "Process reconstructed MC with micro v001 tables", false);
540557

@@ -548,6 +565,9 @@ struct K1AnalysisMicro {
548565
const int charge = part.pdgCode() > 0 ? 1 : -1;
549566
histos.fill(HIST("MCGen/chargeChannel"), charge, static_cast<int>(channel));
550567
histos.fill(HIST("MCGen/ptChannel"), static_cast<int>(channel), part.pt());
568+
if (channel != K1TruthChannel::None) {
569+
histos.fill(HIST("MCGen/response"), part.pt(), static_cast<int>(channel), charge, collision.cent());
570+
}
551571
});
552572
}
553573
PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process generated K1 in selected events with v001 parents", false);

‎PWGLF/Tasks/Resonances/k1TrainingTable.cxx‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ struct K1TrainingTable {
182182

183183
template <bool IsMC, typename Collision, typename Kaon, typename Pion>
184184
void writeK1MlCandidate(Collision const& collision, Kaon const& kaon, Pion const& samePion, Pion const& oppPion,
185-
K1TruthChannel channel, uint16_t passBits)
185+
K1TruthChannel channel, uint16_t passBits, const K1ParentTruthMap* parentTruth = nullptr)
186186
{
187187
using o2::analysis::k1ml::BuildStatus;
188188
const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(kaon), o2::analysis::k1ml::makeTrackSnapshot(samePion), o2::analysis::k1ml::makeTrackSnapshot(oppPion));
@@ -215,14 +215,17 @@ struct K1TrainingTable {
215215
if constexpr (IsMC) {
216216
const bool matched = channel != K1TruthChannel::None;
217217
int motherId = -1;
218+
const K1ParentTruth* parent = nullptr;
218219
if (matched) {
219-
motherId = channel == K1TruthChannel::RhoK ? kaon.motherId() : std::abs(samePion.motherPDG()) == Pdg::kK1_1270Plus ? samePion.motherId()
220-
: oppPion.motherId();
220+
motherId = k1MotherId(channel, kaon, samePion, oppPion);
221+
const int charge = kaon.pdgCode() > 0 ? 1 : -1;
222+
parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr;
221223
}
222224
// Immediate-mother information alone does not prove a physical UID.
223225
k1MlTruth(row, matched ? uint8_t{1} : uint8_t{2}, static_cast<uint8_t>(channel),
224-
matched ? kaon.sign() * Pdg::kK1_1270Plus : 0, static_cast<int64_t>(motherId),
225-
std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN());
226+
parent ? parent->pdgCode : (matched ? kaon.sign() * Pdg::kK1_1270Plus : 0), static_cast<int64_t>(motherId),
227+
parent ? static_cast<float>(parent->pt) : std::numeric_limits<float>::quiet_NaN(),
228+
parent ? static_cast<float>(parent->y) : std::numeric_limits<float>::quiet_NaN());
226229
} else {
227230
k1MlTruth(row, uint8_t{0}, uint8_t{0}, 0, int64_t{-1},
228231
std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN());
@@ -244,7 +247,7 @@ struct K1TrainingTable {
244247
}
245248
PROCESS_SWITCH(K1TrainingTable, processResoMicroTracks, "Write K1 candidates from data micro v001 tables", true);
246249

247-
void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks)
250+
void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents)
248251
{
249252
// The modular producer already selected these reconstructed collisions.
250253
if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) {
@@ -253,10 +256,11 @@ struct K1TrainingTable {
253256
histos.fill(HIST("MCReco/collisions"), 0.5);
254257
histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size());
255258
writeK1MlEvent(collision);
259+
const auto parentTruth = makeK1ParentTruthMap(resoParents);
256260
core.forEachCandidate<true, false, true>(histos, collision, tracks, tracks, false, nullptr, nullptr,
257-
[this](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion,
258-
K1TruthChannel channel, uint16_t passBits) {
259-
writeK1MlCandidate<true>(coll, kaon, samePion, oppPion, channel, passBits);
261+
[this, &parentTruth](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion,
262+
K1TruthChannel channel, uint16_t passBits) {
263+
writeK1MlCandidate<true>(coll, kaon, samePion, oppPion, channel, passBits, &parentTruth);
260264
});
261265
}
262266
PROCESS_SWITCH(K1TrainingTable, processMCMicro, "Write K1 candidates with truth from reconstructed MC micro v001 tables", false);

0 commit comments

Comments
 (0)