From 76c71a74f949e3c2678eba2b02984e1db12555ff Mon Sep 17 00:00:00 2001 From: Luigi Dello Stritto Date: Wed, 30 Jul 2025 12:23:14 +0200 Subject: [PATCH 1/5] Saving ML score in LcpK0s tree creator --- PWGHF/DataModel/CandidateSelectionTables.h | 4 +- .../candidateSelectorLcToK0sP.cxx | 47 +++++-- PWGHF/TableProducer/treeCreatorLcToK0sP.cxx | 125 ++++++++++++------ 3 files changed, 122 insertions(+), 54 deletions(-) diff --git a/PWGHF/DataModel/CandidateSelectionTables.h b/PWGHF/DataModel/CandidateSelectionTables.h index 7f5786ee1f6..125b0fb7286 100644 --- a/PWGHF/DataModel/CandidateSelectionTables.h +++ b/PWGHF/DataModel/CandidateSelectionTables.h @@ -231,10 +231,12 @@ DECLARE_SOA_TABLE(HfSelJpsi, "AOD", "HFSELJPSI", //! namespace hf_sel_candidate_lc_to_k0s_p { DECLARE_SOA_COLUMN(IsSelLcToK0sP, isSelLcToK0sP, int); +DECLARE_SOA_COLUMN(MlProbLcToK0sP, mlProbLcToK0sP, std::vector); //! } // namespace hf_sel_candidate_lc_to_k0s_p - DECLARE_SOA_TABLE(HfSelLcToK0sP, "AOD", "HFSELLCK0SP", //! hf_sel_candidate_lc_to_k0s_p::IsSelLcToK0sP); +DECLARE_SOA_TABLE(HfMlLcToK0sP, "AOD", "HFMLLcK0sP", //! + hf_sel_candidate_lc_to_k0s_p::MlProbLcToK0sP); namespace hf_sel_candidate_b0 { diff --git a/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx b/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx index a4a026a005b..b1f09f44ad3 100644 --- a/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx +++ b/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx @@ -54,6 +54,7 @@ using namespace o2::framework; struct HfCandidateSelectorLcToK0sP { Produces hfSelLcToK0sPCandidate; + Produces hfMlLcToK0sPCandidate; Configurable ptCandMin{"ptCandMin", 0., "Lower bound of candidate pT"}; Configurable ptCandMax{"ptCandMax", 50., "Upper bound of candidate pT"}; @@ -95,6 +96,7 @@ struct HfCandidateSelectorLcToK0sP { TrackSelectorPr selectorProtonHighP; o2::analysis::HfMlResponseLcToK0sP hfMlResponse; + std::vector outputMl = {}; o2::ccdb::CcdbApi ccdbApi; @@ -239,12 +241,11 @@ struct HfCandidateSelectorLcToK0sP { } template - bool selectionMl(const T& hfCandCascade, const U& bach) + bool selectionMl(const T& hfCandCascade, const U& bach, std::vector& outputMl) { auto ptCand = hfCandCascade.pt(); std::vector inputFeatures = hfMlResponse.getInputFeatures(hfCandCascade, bach); - std::vector outputMl = {}; bool isSelectedMl = hfMlResponse.isSelectedMl(inputFeatures, ptCand, outputMl); @@ -265,26 +266,39 @@ struct HfCandidateSelectorLcToK0sP { const auto& bach = candidate.prong0_as(); // bachelor track statusLc = 0; + outputMl.clear(); // implement filter bit 4 cut - should be done before this task at the track selection level // need to add special cuts (additional cuts on decay length and d0 norm) if (!selectionTopol(candidate)) { hfSelLcToK0sPCandidate(statusLc); + if (applyMl) { + hfMlLcToK0sPCandidate(outputMl); + } continue; } if (!selectionStandardPID(bach)) { hfSelLcToK0sPCandidate(statusLc); + if (applyMl) { + hfMlLcToK0sPCandidate(outputMl); + } continue; } - if (applyMl && !selectionMl(candidate, bach)) { - hfSelLcToK0sPCandidate(statusLc); - continue; + bool isSelectedMlLcToK0sP = true; + if (applyMl) { + isSelectedMlLcToK0sP = false; + isSelectedMlLcToK0sP = selectionMl(candidate, bach, outputMl); + hfMlLcToK0sPCandidate(outputMl); + + if (!isSelectedMlLcToK0sP) { + hfSelLcToK0sPCandidate(statusLc); + continue; + } } statusLc = 1; - hfSelLcToK0sPCandidate(statusLc); } } @@ -299,24 +313,37 @@ struct HfCandidateSelectorLcToK0sP { const auto& bach = candidate.prong0_as(); // bachelor track statusLc = 0; + outputMl.clear(); if (!selectionTopol(candidate)) { hfSelLcToK0sPCandidate(statusLc); + if (applyMl) { + hfMlLcToK0sPCandidate(outputMl); + } continue; } if (!selectionBayesPID(bach)) { hfSelLcToK0sPCandidate(statusLc); + if (applyMl) { + hfMlLcToK0sPCandidate(outputMl); + } continue; } - if (applyMl && !selectionMl(candidate, bach)) { - hfSelLcToK0sPCandidate(statusLc); - continue; + bool isSelectedMlLcToK0sP = true; + if (applyMl) { + isSelectedMlLcToK0sP = false; + isSelectedMlLcToK0sP = selectionMl(candidate, bach, outputMl); + hfMlLcToK0sPCandidate(outputMl); + + if (!isSelectedMlLcToK0sP) { + hfSelLcToK0sPCandidate(statusLc); + continue; + } } statusLc = 1; - hfSelLcToK0sPCandidate(statusLc); } } diff --git a/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx b/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx index 0012108a45c..65aafd3cb71 100644 --- a/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx +++ b/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx @@ -33,8 +33,10 @@ #include #include +#include #include #include +#include using namespace o2; using namespace o2::framework; @@ -84,6 +86,9 @@ DECLARE_SOA_COLUMN(V0CtLambda, v0CtLambda, float); DECLARE_SOA_COLUMN(FlagMc, flagMc, int8_t); DECLARE_SOA_COLUMN(OriginMcRec, originMcRec, int8_t); DECLARE_SOA_COLUMN(OriginMcGen, originMcGen, int8_t); +DECLARE_SOA_COLUMN(MlScoreFirstClass, mlScoreFirstClass, float); +DECLARE_SOA_COLUMN(MlScoreSecondClass, mlScoreSecondClass, float); +DECLARE_SOA_COLUMN(MlScoreThirdClass, mlScoreThirdClass, float); // Events DECLARE_SOA_COLUMN(IsEventReject, isEventReject, int); DECLARE_SOA_COLUMN(RunNumber, runNumber, int); @@ -126,7 +131,10 @@ DECLARE_SOA_TABLE(HfCandCascLites, "AOD", "HFCANDCASCLITE", full::Y, full::E, full::FlagMc, - full::OriginMcRec); + full::OriginMcRec, + full::MlScoreFirstClass, + full::MlScoreSecondClass, + full::MlScoreThirdClass); DECLARE_SOA_TABLE(HfCandCascFulls, "AOD", "HFCANDCASCFULL", collision::BCId, @@ -196,7 +204,10 @@ DECLARE_SOA_TABLE(HfCandCascFulls, "AOD", "HFCANDCASCFULL", full::Y, full::E, full::FlagMc, - full::OriginMcRec); + full::OriginMcRec, + full::MlScoreFirstClass, + full::MlScoreSecondClass, + full::MlScoreThirdClass); DECLARE_SOA_TABLE(HfCandCascFullEs, "AOD", "HFCANDCASCFULLE", collision::BCId, @@ -228,6 +239,9 @@ struct HfTreeCreatorLcToK0sP { Configurable ptMaxForDownSample{"ptMaxForDownSample", 24., "Maximum pt for the application of the downsampling factor"}; Configurable fillOnlySignal{"fillOnlySignal", false, "Flag to fill derived tables with signal for ML trainings"}; Configurable fillOnlyBackground{"fillOnlyBackground", false, "Flag to fill derived tables with background for ML trainings"}; + Configurable applyMl{"applyMl", false, "Whether ML was used in candidateSelectorLc"}; + + constexpr static float UndefValueFloat = -999.f; HfHelper hfHelper; @@ -235,16 +249,46 @@ struct HfTreeCreatorLcToK0sP { using TracksWPid = soa::Join; using SelectedCandidatesMc = soa::Filtered>; - Partition recSig = nabs(aod::hf_cand_casc::flagMcMatchRec) != int8_t(0); - Partition recBkg = nabs(aod::hf_cand_casc::flagMcMatchRec) == int8_t(0); - void init(InitContext const&) { } + /// \brief function to get ML score values for the current candidate and assign them to input parameters + /// \param candidate candidate instance + /// \param candidateMlScore instance of handler of vectors with ML scores associated with the current candidate + /// \param mlScoreFirstClass ML score for belonging to the first class + /// \param mlScoreSecondClass ML score for belonging to the second class + /// \param mlScoreThirdClass ML score for belonging to the third class + void assignMlScores(aod::HfMlLcToK0sP::iterator const& candidateMlScore, float& mlScoreFirstClass, float& mlScoreSecondClass, float& mlScoreThirdClass) + { + std::vector mlScores; + std::copy(candidateMlScore.mlProbLcToK0sP().begin(), candidateMlScore.mlProbLcToK0sP().end(), std::back_inserter(mlScores)); + + constexpr int IndexFirstClass{0}; + constexpr int IndexSecondClass{1}; + constexpr int IndexThirdClass{2}; + if (mlScores.size() == 0) { + return; // when candidateSelectorLcK0sP rejects a candidate by "usual", non-ML cut, the ml score vector remains empty + } + mlScoreFirstClass = mlScores.at(IndexFirstClass); + mlScoreSecondClass = mlScores.at(IndexSecondClass); + if (mlScores.size() > IndexThirdClass) { + mlScoreThirdClass = mlScores.at(IndexThirdClass); + } + } + template - void fillCandidate(const T& candidate, const U& bach, int8_t flagMc, int8_t originMcRec) + void fillCandidate(const T& candidate, const U& bach, int8_t flagMc, int8_t originMcRec, aod::HfMlLcToK0sP::iterator const& candidateMlScore) { + + float mlScoreFirstClass{UndefValueFloat}; + float mlScoreSecondClass{UndefValueFloat}; + float mlScoreThirdClass{UndefValueFloat}; + + if (applyMl) { + assignMlScores(candidateMlScore, mlScoreFirstClass, mlScoreSecondClass, mlScoreThirdClass); + } + if (fillCandidateLiteTable) { rowCandidateLite( candidate.chi2PCA(), @@ -283,7 +327,10 @@ struct HfTreeCreatorLcToK0sP { hfHelper.yLc(candidate), hfHelper.eLc(candidate), flagMc, - originMcRec); + originMcRec, + mlScoreFirstClass, + mlScoreSecondClass, + mlScoreThirdClass); } else { rowCandidateFull( bach.collision().bcId(), @@ -353,7 +400,10 @@ struct HfTreeCreatorLcToK0sP { hfHelper.yLc(candidate), hfHelper.eLc(candidate), flagMc, - originMcRec); + originMcRec, + mlScoreFirstClass, + mlScoreSecondClass, + mlScoreThirdClass); } } template @@ -370,6 +420,7 @@ struct HfTreeCreatorLcToK0sP { void processMc(aod::Collisions const& collisions, aod::McCollisions const&, SelectedCandidatesMc const& candidates, + aod::HfMlLcToK0sP const& candidateMlScores, soa::Join const& particles, TracksWPid const&) { @@ -380,42 +431,25 @@ struct HfTreeCreatorLcToK0sP { fillEvent(collision); } - if (fillOnlySignal) { - if (fillCandidateLiteTable) { - rowCandidateLite.reserve(recSig.size()); - } else { - rowCandidateFull.reserve(recSig.size()); - } - for (const auto& candidate : recSig) { - auto bach = candidate.prong0_as(); // bachelor - fillCandidate(candidate, bach, candidate.flagMcMatchRec(), candidate.originMcRec()); - } - } else if (fillOnlyBackground) { - if (fillCandidateLiteTable) { - rowCandidateLite.reserve(recBkg.size()); - } else { - rowCandidateFull.reserve(recBkg.size()); - } - for (const auto& candidate : recBkg) { - if (downSampleBkgFactor < 1.) { - float pseudoRndm = candidate.ptProng0() * 1000. - static_cast(candidate.ptProng0() * 1000); - if (candidate.pt() < ptMaxForDownSample && pseudoRndm >= downSampleBkgFactor) { - continue; - } - } - auto bach = candidate.prong0_as(); // bachelor - fillCandidate(candidate, bach, candidate.flagMcMatchRec(), candidate.originMcRec()); - } + if (fillCandidateLiteTable) { + rowCandidateLite.reserve(candidates.size()); } else { - // Filling candidate properties - if (fillCandidateLiteTable) { - rowCandidateLite.reserve(candidates.size()); + rowCandidateFull.reserve(candidates.size()); + } + + int iCand{0}; + for (const auto& candidate : candidates) { + auto candidateMlScore = candidateMlScores.rawIteratorAt(iCand); + ++iCand; + auto bach = candidate.prong0_as(); // bachelor + const int flag = candidate.flagMcMatchRec(); + + if (fillOnlySignal && flag != 0) { + fillCandidate(candidate, bach, candidate.flagMcMatchRec(), candidate.originMcRec(), candidateMlScore); + } else if (fillOnlyBackground && flag == 0) { + fillCandidate(candidate, bach, candidate.flagMcMatchRec(), candidate.originMcRec(), candidateMlScore); } else { - rowCandidateFull.reserve(candidates.size()); - } - for (const auto& candidate : candidates) { - auto bach = candidate.prong0_as(); // bachelor - fillCandidate(candidate, bach, candidate.flagMcMatchRec(), candidate.originMcRec()); + fillCandidate(candidate, bach, candidate.flagMcMatchRec(), candidate.originMcRec(), candidateMlScore); } } @@ -439,6 +473,7 @@ struct HfTreeCreatorLcToK0sP { void processData(aod::Collisions const& collisions, soa::Join const& candidates, + aod::HfMlLcToK0sP const& candidateMlScores, TracksWPid const&) { @@ -454,11 +489,15 @@ struct HfTreeCreatorLcToK0sP { } else { rowCandidateFull.reserve(candidates.size()); } + + int iCand{0}; for (const auto& candidate : candidates) { + auto candidateMlScore = candidateMlScores.rawIteratorAt(iCand); + ++iCand; auto bach = candidate.prong0_as(); // bachelor double pseudoRndm = bach.pt() * 1000. - static_cast(bach.pt() * 1000); if (candidate.isSelLcToK0sP() >= 1 && pseudoRndm < downSampleBkgFactor) { - fillCandidate(candidate, bach, 0, 0); + fillCandidate(candidate, bach, 0, 0, candidateMlScore); } } } From 5eff03fc08e854a477ec10fe21596f8be545c59d Mon Sep 17 00:00:00 2001 From: Luigi Dello Stritto Date: Wed, 30 Jul 2025 13:17:24 +0200 Subject: [PATCH 2/5] fix --- PWGHF/TableProducer/treeCreatorLcToK0sP.cxx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx b/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx index 65aafd3cb71..9e0ec5f0367 100644 --- a/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx +++ b/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx @@ -70,8 +70,8 @@ DECLARE_SOA_COLUMN(DecayLength, decayLength, float); DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); DECLARE_SOA_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, float); -DECLARE_SOA_COLUMN(CPA, cpa, float); -DECLARE_SOA_COLUMN(CPAXY, cpaXY, float); +DECLARE_SOA_COLUMN(Cpa, cpa, float); +DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); DECLARE_SOA_COLUMN(Ct, ct, float); DECLARE_SOA_COLUMN(PtV0Pos, ptV0Pos, float); DECLARE_SOA_COLUMN(PtV0Neg, ptV0Neg, float); @@ -123,8 +123,8 @@ DECLARE_SOA_TABLE(HfCandCascLites, "AOD", "HFCANDCASCLITE", full::NSigmaTOFPr0, full::M, full::Pt, - full::CPA, - full::CPAXY, + full::Cpa, + full::CpaXY, full::Ct, full::Eta, full::Phi, @@ -196,8 +196,8 @@ DECLARE_SOA_TABLE(HfCandCascFulls, "AOD", "HFCANDCASCFULL", full::M, full::Pt, full::P, - full::CPA, - full::CPAXY, + full::Cpa, + full::CpaXY, full::Ct, full::Eta, full::Phi, @@ -245,9 +245,9 @@ struct HfTreeCreatorLcToK0sP { HfHelper hfHelper; - Filter filterSelectCandidates = aod::hf_sel_candidate_lc_to_k0s_p::isSelLcToK0sP >= 1; using TracksWPid = soa::Join; using SelectedCandidatesMc = soa::Filtered>; + Filter filterSelectCandidates = aod::hf_sel_candidate_lc_to_k0s_p::isSelLcToK0sP >= 1; void init(InitContext const&) { From 71309fe100f0b396ef0583d85d79b78e695df828 Mon Sep 17 00:00:00 2001 From: ldellost <47105254+DelloStritto@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:59:43 +0200 Subject: [PATCH 3/5] Update PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vít Kučera --- PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx b/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx index b1f09f44ad3..cae97e73d1e 100644 --- a/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx +++ b/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx @@ -286,10 +286,8 @@ struct HfCandidateSelectorLcToK0sP { continue; } - bool isSelectedMlLcToK0sP = true; if (applyMl) { - isSelectedMlLcToK0sP = false; - isSelectedMlLcToK0sP = selectionMl(candidate, bach, outputMl); + bool isSelectedMlLcToK0sP = selectionMl(candidate, bach, outputMl); hfMlLcToK0sPCandidate(outputMl); if (!isSelectedMlLcToK0sP) { From d41a59c6e7121e7371686f6fe9c432c6ff695b17 Mon Sep 17 00:00:00 2001 From: ldellost <47105254+DelloStritto@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:59:58 +0200 Subject: [PATCH 4/5] Update PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vít Kučera --- PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx b/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx index cae97e73d1e..d91ab2f718e 100644 --- a/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx +++ b/PWGHF/TableProducer/candidateSelectorLcToK0sP.cxx @@ -329,10 +329,8 @@ struct HfCandidateSelectorLcToK0sP { continue; } - bool isSelectedMlLcToK0sP = true; if (applyMl) { - isSelectedMlLcToK0sP = false; - isSelectedMlLcToK0sP = selectionMl(candidate, bach, outputMl); + bool isSelectedMlLcToK0sP = selectionMl(candidate, bach, outputMl); hfMlLcToK0sPCandidate(outputMl); if (!isSelectedMlLcToK0sP) { From 4a1954039f717f4ba1a35fa705867d9348c3a383 Mon Sep 17 00:00:00 2001 From: Luigi Dello Stritto Date: Tue, 26 Aug 2025 11:43:40 +0200 Subject: [PATCH 5/5] checking the that the configurable are correct --- PWGHF/TableProducer/treeCreatorLcToK0sP.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx b/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx index 9e0ec5f0367..1e2b2484c13 100644 --- a/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx +++ b/PWGHF/TableProducer/treeCreatorLcToK0sP.cxx @@ -425,6 +425,11 @@ struct HfTreeCreatorLcToK0sP { TracksWPid const&) { + if (applyMl && candidateMlScores.size() == 0) { + LOG(fatal) << "ML enabled but table with the ML scores is empty! Please check your configurables."; + return; + } + // Filling event properties rowCandidateFullEvents.reserve(collisions.size()); for (const auto& collision : collisions) { @@ -477,6 +482,11 @@ struct HfTreeCreatorLcToK0sP { TracksWPid const&) { + if (applyMl && candidateMlScores.size() == 0) { + LOG(fatal) << "ML enabled but table with the ML scores is empty! Please check your configurables."; + return; + } + // Filling event properties rowCandidateFullEvents.reserve(collisions.size()); for (const auto& collision : collisions) {