From 52fb022465a56e8f9722dd8ccc6b92598cce1a78 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Mon, 21 Sep 2026 10:48:09 +0200 Subject: [PATCH 01/23] PWGHF: Add charm-charm FemtoDream workflows --- PWGHF/HFC/TableProducer/CMakeLists.txt | 5 + .../producerCharmHadronsCharmFemtoDream.cxx | 398 +++++++++++++++ PWGHF/HFC/Tasks/CMakeLists.txt | 5 + .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 476 ++++++++++++++++++ 4 files changed, 884 insertions(+) create mode 100644 PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx create mode 100644 PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx diff --git a/PWGHF/HFC/TableProducer/CMakeLists.txt b/PWGHF/HFC/TableProducer/CMakeLists.txt index d0c1fb800ff..299981d8c1f 100644 --- a/PWGHF/HFC/TableProducer/CMakeLists.txt +++ b/PWGHF/HFC/TableProducer/CMakeLists.txt @@ -98,3 +98,8 @@ o2physics_add_dpl_workflow(producer-charm-hadrons-v0-femto-dream SOURCES producerCharmHadronsV0FemtoDream.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(producer-charm-hadrons-charm-femto-dream + SOURCES producerCharmHadronsCharmFemtoDream.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx new file mode 100644 index 00000000000..5fe9f773306 --- /dev/null +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -0,0 +1,398 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file producerCharmHadronsCharmFemtoDream.cxx +/// \brief Produce reduced D-meson candidate tables for charm-charm femtoscopy +/// \author Biao Zhang, Heidelberg University, biao.zhang@cern.ch + +#include "PWGCF/DataModel/FemtoDerived.h" +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/HfMlResponseD0ToKPi.h" +#include "PWGHF/Core/HfMlResponseDstarToD0Pi.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsBfieldCCDB.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/Core/ZorroSummary.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct HfProducerCharmHadronsCharmFemtoDream { + enum MlMode : uint8_t { + NoMl = 0, + FillMlFromSelector = 1, + FillMlFromNewBDT = 2 + }; + // Each species needs its own model, feature order and pT-dependent cuts. + struct MlConfig : ConfigurableGroup { + std::string prefix; + static constexpr double DefaultCuts[3] = {1., 0., 0.}; + Configurable applyMlMode{"applyMlMode", FillMlFromSelector, "0: no ML, 1: selector scores, 2: new BDT after selector"}; + Configurable> binsPtMl{"binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; + Configurable> cutsMl{"cutsMl", {DefaultCuts, 1, 3}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; + Configurable> cutDirMl{"cutDirMl", std::vector{0, 1, 1}, "Reject scores above (0), below (1), or do not cut (2)"}; + Configurable nClassesMl{"nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{}, "Ordered input feature names for new BDT"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{}, "Model files, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{}, "CCDB model paths, one per pT bin"}; + Configurable timestampCCDB{"timestampCCDB", -1, "Timestamp used to retrieve models"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Load new BDT from CCDB instead of local files"}; + } mlD0{{}, "mlD0"}, mlDstar{{}, "mlDstar"}; + o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; + o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; + o2::ccdb::CcdbApi ccdbApi; + + Produces collisions; + Produces rowMasks; + bool hasD0 = false; + bool hasDstar = false; + Produces d0Rows; + Produces dstarRows; + Configurable selectionFlagD0{"selectionFlagD0", 1, "Minimum D0 selector decision"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; + Service ccdb; + o2::hf_evsel::HfEventSelection hfEvSel; + OutputObj zorroSummary{"zorroSummary"}; + HistogramRegistry registry{"QA", {}, OutputObjHandlingPolicy::AnalysisObject}; + int runNumber = -1; + using Collisions = + soa::Join; + using CollisionsWithFT0C = soa::Join; + using D0s = soa::Join; + using Dstars = + soa::Join; + + using D0sMl = soa::Join; + using DstarsMl = soa::Join; + + Filter filterSelectCandidateD0 = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); + Filter filterSelectCandidateDstar = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + + template + void initMl(MlConfig const& cfg, Response& response, bool withMl) + { + if (cfg.applyMlMode.value < NoMl || cfg.applyMlMode.value > FillMlFromNewBDT) { + LOGP(fatal, "{}: invalid applyMlMode", cfg.prefix); + } + if (cfg.applyMlMode.value != FillMlFromNewBDT) { + return; + } + if (!withMl) { + LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); + } + auto const& edges = cfg.binsPtMl.value; + if (edges.size() < 2 || + !std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }) || + std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { return a >= b; }) != edges.end()) { + LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", cfg.prefix); + } + const auto nBins = edges.size() - 1; + if (cfg.nClassesMl.value != 3 || cfg.cutDirMl.value.size() != 3 || + cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != 3 || + cfg.onnxFileNames.value.size() != nBins || cfg.namesInputFeatures.value.empty() || + (cfg.loadModelsFromCCDB.value && cfg.modelPathsCCDB.value.size() != nBins)) { + LOGP(fatal, "{}: provide three classes, cuts/models for every pT bin, and input features", cfg.prefix); + } + for (auto direction : cfg.cutDirMl.value) { + if (direction < o2::cuts_ml::CutGreater || direction > o2::cuts_ml::CutNot) { + LOGP(fatal, "{}: invalid cutDirMl", cfg.prefix); + } + } + for (unsigned int bin = 0; bin < nBins; ++bin) { + for (unsigned int score = 0; score < 3; ++score) { + if (!std::isfinite(cfg.cutsMl.value.get(bin, score))) { + LOGP(fatal, "{}: cutsMl must be finite", cfg.prefix); + } + } + } + response.configure(edges, cfg.cutsMl.value, cfg.cutDirMl.value, cfg.nClassesMl.value); + response.cacheInputFeaturesIndices(cfg.namesInputFeatures.value); + if (cfg.loadModelsFromCCDB.value) { + ccdbApi.init(ccdbUrl.value); + response.setModelPathsCCDB(cfg.onnxFileNames.value, ccdbApi, cfg.modelPathsCCDB.value, cfg.timestampCCDB.value); + } else { + response.setModelPathsLocal(cfg.onnxFileNames.value); + } + response.init(); + } + + void init(InitContext const&) + { + if (static_cast(doprocessD0D0) + doprocessD0Dstar + doprocessD0D0Ml + + doprocessD0DstarMl + doprocessD0D0WithFT0C + doprocessD0DstarWithFT0C + + doprocessD0D0MlWithFT0C + doprocessD0DstarMlWithFT0C != + 1) { + LOGP(fatal, "Enable exactly one charm-charm producer process"); + } + if (selectionFlagD0 < 1) { + LOGP(fatal, "selectionFlagD0 must be positive"); + } + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + const bool withMl = doprocessD0D0Ml || doprocessD0DstarMl || doprocessD0D0MlWithFT0C || doprocessD0DstarMlWithFT0C; + initMl(mlD0, hfMlResponseD0, withMl); + if (doprocessD0Dstar || doprocessD0DstarMl || doprocessD0DstarWithFT0C || doprocessD0DstarMlWithFT0C) { + initMl(mlDstar, hfMlResponseDstar, withMl); + } + hfEvSel.init(registry, &zorroSummary); + registry.add("events", ";stage (0=all,1=accepted);events", kTH1F, + {{2, -0.5, 1.5}}); + registry.add("candidates", ";species (0=D0,1=D0bar,2=Dstar+,3=Dstar-);rows", + kTH1F, {{4, -0.5, 3.5}}); + } + + template + bool acceptCollision(Collision const& col) + { + registry.fill(HIST("events"), 0); + float cent = -1.f; // No centrality for pp MB. + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask< + true, CentEstimator, + aod::BCsWithTimestamps>(col, cent, ccdb, registry); + hfEvSel.fillHistograms(col, rejectionMask, cent); + if (rejectionMask != 0) { + return false; + } + hasD0 = false; + hasDstar = false; + auto bc = col.template bc_as(); + initCCDB(bc, runNumber, ccdb, ccdbPathGrpMag.value, nullptr, false); + // Propagator field is in kG; FemtoDream uses Tesla. + const float fieldTesla = + 0.1f * o2::base::Propagator::Instance()->getNominalBz(); + // MultV0M is the common reduced percentile column: FT0C or -1 (no estimator). + collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, + fieldTesla); + registry.fill(HIST("events"), 1); + // Keep all accepted collisions, including zero/single-candidate events. + return true; + } + + template + std::array readScores(Scores const& scores) + { + if (scores.size() != 3) { + LOGP(fatal, + "Expected three selector ML scores: background, prompt, nonprompt"); + return {-1.f, -1.f, -1.f}; + } + for (auto score : scores) { + if (!std::isfinite(score)) { + LOGP(fatal, "Non-finite selector ML score"); + } + } + return {scores[0], scores[1], scores[2]}; + } + + template + void fillD0(Collision const& col, Candidates const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); + for (auto const& cand : candidates) { + auto p0 = cand.template prong0_as(); + auto p1 = cand.template prong1_as(); + // The OR filter accepts the row if either hypothesis passes. Write only + // the passing hypotheses, each with its own flavour and ML scores. + for (int hypothesis = 0; hypothesis < 2; ++hypothesis) { + if ((hypothesis == 0 ? cand.isSelD0() : cand.isSelD0bar()) < + selectionFlagD0) { + continue; + } + std::array scores{-1.f, -1.f, -1.f}; + if constexpr (WithMl) { + if (mlD0.applyMlMode != NoMl) { + scores = hypothesis == 0 ? readScores(cand.mlProbD0()) + : readScores(cand.mlProbD0bar()); + } + if (mlD0.applyMlMode == FillMlFromNewBDT) { + // Do not call the ML response with an out-of-range model index. + if (!std::isfinite(cand.pt()) || cand.pt() < mlD0.binsPtMl.value.front() || cand.pt() >= mlD0.binsPtMl.value.back()) { + continue; + } + const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 : -o2::constants::physics::kD0; + auto features = hfMlResponseD0.getInputFeatures(cand, pdgCode); + std::vector output; + if (!hfMlResponseD0.isSelectedMl(features, cand.pt(), output)) { + continue; // Reject this hypothesis only. + } + scores = readScores(output); + } + } + d0Rows(collisions.lastIndex(), timestamp, hypothesis == 0 ? 1 : -1, + p0.globalIndex(), p1.globalIndex(), p0.pt(), p1.pt(), p0.eta(), + p1.eta(), p0.phi(), p1.phi(), 1 << hypothesis, scores[0], + scores[1], scores[2]); + hasD0 = true; + registry.fill(HIST("candidates"), hypothesis); + } + } + } + + template + void fillDstar(Collision const& col, + Candidates const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); + for (auto const& cand : candidates) { + auto p0 = cand.template prong0_as(); + auto p1 = cand.template prong1_as(); + auto soft = cand.template prongPi_as(); + std::array scores{-1.f, -1.f, -1.f}; + if constexpr (WithMl) { + if (mlDstar.applyMlMode != NoMl) { + scores = readScores(cand.mlProbDstarToD0Pi()); + } + if (mlDstar.applyMlMode == FillMlFromNewBDT) { + if (!std::isfinite(cand.pt()) || cand.pt() < mlDstar.binsPtMl.value.front() || cand.pt() >= mlDstar.binsPtMl.value.back()) { + continue; + } + // Match the unswapped daughter convention used by D+track. + auto features = hfMlResponseDstar.getInputFeatures(cand, false); + std::vector output; + if (!hfMlResponseDstar.isSelectedMl(features, cand.pt(), output)) { + continue; + } + scores = readScores(output); + } + } + dstarRows(collisions.lastIndex(), timestamp, soft.sign(), + p0.globalIndex(), p1.globalIndex(), soft.globalIndex(), p0.pt(), + p1.pt(), soft.pt(), p0.eta(), p1.eta(), soft.eta(), p0.phi(), + p1.phi(), soft.phi(), 1, scores[0], scores[1], scores[2]); + hasDstar = true; + registry.fill(HIST("candidates"), soft.sign() > 0 ? 2 : 3); + } + } + + void processD0D0(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0,"D0 only, data", true); + + void processD0Dstar(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s, soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); + + void processD0D0Ml(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml,"D0 with selector ML scores", false); + + void processD0DstarMl(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl,"D0 and Dstar with selector ML scores", false); + + void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, "D0 only, data with FT0C centrality", false); + + void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s, soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, "D0 and Dstar, data with FT0C centrality", false); + + void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, "D0 with selector ML scores and FT0C centrality", false); + + void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMlWithFT0C, "D0 and Dstar with selector ML scores and FT0C centrality", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/HFC/Tasks/CMakeLists.txt b/PWGHF/HFC/Tasks/CMakeLists.txt index 4fa2b6cd959..69e279424c9 100644 --- a/PWGHF/HFC/Tasks/CMakeLists.txt +++ b/PWGHF/HFC/Tasks/CMakeLists.txt @@ -70,3 +70,8 @@ o2physics_add_dpl_workflow(task-flow COMPONENT_NAME Analysis) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/PWGCF/Core) + +o2physics_add_dpl_workflow(task-charm-hadrons-charm-femto-dream + SOURCES taskCharmHadronsCharmFemtoDream.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx new file mode 100644 index 00000000000..f89826fc6f1 --- /dev/null +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -0,0 +1,476 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskCharmHadronsCharmFemtoDream.cxx +/// \brief Build DD pair femtoscopy distributions +/// \author Biao Zhang, Heidelberg University, biao.zhang@cern.ch + +#include "PWGCF/DataModel/FemtoDerived.h" +#include "PWGCF/FemtoDream/Core/femtoDreamMath.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; +using namespace o2::analysis::femtoDream; + +struct HfTaskCharmHadronsCharmFemtoDream { + // Like/unlike sign refers to charm flavour; D0 is electrically neutral. + // Keep charge-conjugate channels separate so they can also be combined offline. + enum PairChannel : int { + kD0D0LikeSign = 0, + kD0BarD0BarLikeSign = 1, + kD0D0BarUnlikeSign = 2, + kD0DstarPlusLikeSign = 3, + kD0BarDstarMinusLikeSign = 4, + kD0DstarMinusUnlikeSign = 5, + kD0BarDstarPlusUnlikeSign = 6, + kNPairChannels = 7 + }; + + template + static bool sharesDaughter(D0Row const& a, OtherRow const& b) + { + for (auto id : std::array{a.prong0Id(), a.prong1Id()}) { + if (id < 0) { + continue; + } + if (id == b.prong0Id() || id == b.prong1Id()) { + return true; + } + if constexpr (IsDstar) { + if (id == b.prong2Id()) { + return true; + } + } + } + return false; + } + + // a is D0; b is either D0 or Dstar. D0 charge means flavour, not electric + // charge. + template + static PairChannel pairChannel(D0Row const& a, OtherRow const& b) + { + if constexpr (!IsDstar) { + return a.charge() != b.charge() ? kD0D0BarUnlikeSign : (a.charge() > 0 ? kD0D0LikeSign : kD0BarD0BarLikeSign); + } + return a.charge() > 0 ? (b.charge() > 0 ? kD0DstarPlusLikeSign : kD0DstarMinusUnlikeSign) + : (b.charge() < 0 ? kD0BarDstarMinusLikeSign : kD0BarDstarPlusUnlikeSign); + } + + // Canonical ordering keeps mass/pt axes independent of event ordering for D0D0. + template + static bool reverseD0Order(FirstRow const& a, SecondRow const& b) + { + if (a.charge() != b.charge()) { + return a.charge() < b.charge(); // D0 first for D0-D0bar + } + return a.pt() < b.pt(); // leading pT first for equal flavours + } + + Configurable ptMinD0{"ptMinD0", 0.f, "Minimum D0 pT"}; + Configurable ptMaxD0{"ptMaxD0", 36.f, "Maximum D0 pT"}; + Configurable ptMinDstar{"ptMinDstar", 0.f, "Minimum Dstar pT"}; + Configurable ptMaxDstar{"ptMaxDstar", 36.f, "Maximum Dstar pT"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute candidate eta"}; + Configurable massMinD0{"massMinD0", 1.7f, "Minimum D0 mass (keep sidebands)"}; + Configurable massMaxD0{"massMaxD0", 2.0f, "Maximum D0 mass"}; + Configurable deltaMassMin{"deltaMassMin", 0.139f, "Minimum Dstar-D0 mass difference"}; + Configurable deltaMassMax{"deltaMassMax", 0.17f, "Maximum Dstar-D0 mass difference"}; + Configurable daughterMassMin{"daughterMassMin", 1.80f,"Minimum Dstar daughter D0 mass"}; + Configurable daughterMassMax{"daughterMassMax", 1.93f,"Maximum Dstar daughter D0 mass"}; + Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; + Configurable maxBkgD0{"maxBkgD0", 1.f, "Maximum D0 background score"}; + Configurable minPromptD0{"minPromptD0", 0.f, "Minimum D0 prompt score"}; + Configurable maxBkgDstar{"maxBkgDstar", 1.f,"Maximum Dstar background score"}; + Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; + Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; + struct : ConfigurableGroup { + std::string prefix = "eventSel"; + Configurable useCentrality{"useCentrality", false, "Apply percentile selection (requires a centrality-enabled producer)"}; + Configurable multMin{"multMin", 0, "Minimum multiplicity (MultNtr)"}; + Configurable multMax{"multMax", 99999, "Maximum multiplicity (MultNtr)"}; + Configurable multPercentileMin{"multPercentileMin", 0.f, "Minimum multiplicity percentile"}; + Configurable multPercentileMax{"multPercentileMax", 100.f, "Maximum multiplicity percentile"}; + } eventSel; + + struct : ConfigurableGroup { + Configurable doMixEvent{"doMixEvent", true, "Enable mixed events"}; + Configurable mixingBinPolicy{"mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; + Configurable mixingDepth{"mixingDepth", 5, "Number of neighbours for O2 event mixing"}; + } mixSetting; + ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; + ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; + ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; + using FilteredCollisions = soa::Filtered>; + using FilteredCharmCand2Prongs = soa::Filtered; + using FilteredCharmCandDstars = soa::Filtered; + + Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= eventSel.multMin && aod::femtodreamcollision::multNtr <= eventSel.multMax; + Filter eventMultiplicityPercentile = ifnode(eventSel.useCentrality, aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, Node{LiteralNode{true}}); + Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmHadCandSel; + + Partition partitionCharmHadron2Prong = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, Node{LiteralNode{true}}); + Partition partitionCharmHadronDstar = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgDstar && aod::fdhf::bdtPrompt >= minPromptDstar, Node{LiteralNode{true}}); + + // Full-table process inputs do not automatically register grouping caches. + // Register both keys explicitly for the sliceByCached calls below. + Preslice perCollisionD0 = aod::femtodreamparticle::fdCollisionId; + Preslice perCollisionDstar = aod::femtodreamparticle::fdCollisionId; + SliceCache cache; + HistogramRegistry registry{ + "Results", + {}, + OutputObjHandlingPolicy::AnalysisObject}; + Produces rowFemtoResultCharm2Prong; + Produces rowFemtoResultCharmDstar; + Produces rowFemtoResultColl; + + void init(InitContext const&) + { + if (static_cast(doprocessD0D0) + doprocessD0Dstar != 1) { + LOGP(fatal, "Enable exactly one charm-charm analysis process"); + } + if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > 2 || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || + ptMinDstar < 0 || ptMinDstar >= ptMaxDstar || etaMax <= 0 || + massMinD0 >= massMaxD0 || deltaMassMin >= deltaMassMax || + daughterMassMin >= daughterMassMax || charmHadCandSel < 1 || + eventSel.multMin > eventSel.multMax || eventSel.multPercentileMin > eventSel.multPercentileMax) { + LOGP(fatal, "Invalid charm-charm selection or mixing configuration"); + } + colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; + const AxisSpec massD0{300, massMinD0.value, massMaxD0.value, + "M(Kpi) (GeV/c2)"}; + const AxisSpec deltaMass{310, deltaMassMin.value, deltaMassMax.value, + "Delta M (GeV/c2)"}; + const AxisSpec pt{72, ptMinD0.value, ptMaxD0.value, "D0 pT (GeV/c)"}; + const AxisSpec ptStar{72, ptMinDstar.value, ptMaxDstar.value, "Dstar pT (GeV/c)"}; + const AxisSpec mult{mixingBinMult, "NTracksPV"}; + const AxisSpec channel{ + kNPairChannels, -0.5, kNPairChannels - 0.5, + "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; + registry.add("SE/D0D0", "Same event", kTHnSparseF, + {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("ME/D0D0", "Mixed event", kTHnSparseF, + {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("SE/D0Dstar", "Same event", kTHnSparseF, + {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("ME/D0Dstar", "Mixed event", kTHnSparseF, + {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("QA/events", ";0=in mixing range,1=outside;events", kTH1F, + {{2, -0.5, 1.5}}); + registry.add("MixingQA/hSECollisionBins", ";mixing bin;events", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0D0", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0Dstar", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("QA/sharedDaughters", ";0=D0D0,1=D0Dstar;rejected pairs", + kTH1F, {{2, -0.5, 1.5}}); + registry.add("QA/d0MassPt", ";M(Kpi);pT", kTH2F, {massD0, pt}); + registry.add("QA/dstarMassPt", ";Delta M;pT", kTH2F, {deltaMass, ptStar}); + } + + template + void validateMlScores(Candidates const& candidates) + { + if (!useMl) { + return; + } + for (auto const& row : candidates) { + if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || + row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { + LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML producer process"); + } + } + } + + template + bool select(Row const& row, float& mass) + { + if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || + !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || + std::abs(row.eta()) >= etaMax || + row.pt() < (IsDstar ? ptMinDstar.value : ptMinD0.value) || + row.pt() >= (IsDstar ? ptMaxDstar.value : ptMaxD0.value)) { + return false; + } + const std::array masses = + row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} + : std::array{MassKPlus, MassPiPlus}; + if constexpr (IsDstar) { + const float daughterMass = row.mDaughD0(masses); + if (!std::isfinite(daughterMass) || daughterMass < daughterMassMin || + daughterMass >= daughterMassMax) { + return false; + } + mass = row.m(std::array{masses[0], masses[1], MassPiPlus}) - daughterMass; + } else { + mass = row.m(masses); + } + if (!std::isfinite(mass) || + mass < (IsDstar ? deltaMassMin.value : massMinD0.value) || + mass >= (IsDstar ? deltaMassMax.value : massMaxD0.value)) { + return false; + } + if constexpr (IsDstar) { + if constexpr (FillQa) { + registry.fill(HIST("QA/dstarMassPt"), mass, row.pt()); + } + } else { + if constexpr (FillQa) { + registry.fill(HIST("QA/d0MassPt"), mass, row.pt()); + } + } + return true; + } + + template + void fillPair(FirstRow const& first, SecondRow const& second, + float firstMass, float secondMass, float mult) + { + if constexpr (!Mixed) { + if (sharesDaughter(first, second)) { + registry.fill(HIST("QA/sharedDaughters"), IsDstar ? 1 : 0); + return; + } + } + const bool reverse = !IsDstar && reverseD0Order(first, second); + const float mass1 = reverse ? secondMass : firstMass; + const float mass2 = reverse ? firstMass : secondMass; + const float pt1 = reverse ? second.pt() : first.pt(); + const float pt2 = reverse ? first.pt() : second.pt(); + // Read kinematics directly from the candidate rows. Only D0D0 can reverse; + // its two nominal parent masses are equal. Never use Dstar delta mass here. + const float kstar = reverse + ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) + : FemtoDreamMath::getkstar(first, MassD0, second, IsDstar ? MassDStar : MassD0); + if (!std::isfinite(kstar)) { + return; + } + const int channel = pairChannel(first, second); + if constexpr (IsDstar && Mixed) { + registry.fill(HIST("ME/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else if constexpr (IsDstar) { + registry.fill(HIST("SE/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else if constexpr (Mixed) { + registry.fill(HIST("ME/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else { + registry.fill(HIST("SE/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } + } + + template + int mixingBin(Policy const& policy, Collision col) + { + return policy.getBin(policy.getBinningValues(col)); + } + + template + void doMixedEvent(FilteredCollisions const& cols, D0Table const& d0Rows, + OtherTable const& otherRows, Policy const& policy) + { + Partition partitionMaskedCol1 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. + Partition partitionMaskedCol2 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + Partition partitionMaskedColDstar = (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; + partitionMaskedCol1.bindTable(cols); + if constexpr (IsDstar) { + partitionMaskedColDstar.bindTable(cols); + } else { + partitionMaskedCol2.bindTable(cols); + } + auto const& secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered : *partitionMaskedCol2.mFiltered; + for (auto const& [collision1, collision2] : combinations(soa::CombinationsBlockFullIndexPolicy(policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, secondCollisions))) { + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } + if constexpr (!IsDstar) { + // Full policy returns both orientations for identical inputs. Count once. + if (collision1.globalIndex() > collision2.globalIndex()) { + continue; + } + } + if (!std::isfinite(collision1.magField()) || collision1.magField() != collision2.magField()) { + continue; + } + if constexpr (IsDstar) { + registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), mixingBin(policy, collision1)); + } else { + registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), mixingBin(policy, collision1)); + } + auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); + auto second = otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision2.globalIndex(), cache); + // Species roles remain fixed; the full policy supplies the reverse event + // orientation when eligible. Do not add a second manual reverse loop. + for (auto const& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(first, second))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, collision1.multNtr()); + } + } + } + } + + // Write candidates once per collision, independently of SE/ME pair counts. + // Reuse the same output tables and field conventions as the track/V0 tasks. + template + void fillTables(CollType const& col, D0Slice const& d0s, DstarSlice const& dstars) + { + int64_t timeStamp = -1; + bool hasCandidate = false; + auto recordTimeStamp = [&](int64_t value) { + if (hasCandidate && value != timeStamp) { + LOGP(fatal, "Inconsistent charm timestamps in one reduced collision"); + } + timeStamp = value; + hasCandidate = true; + }; + for (auto const& part : d0s) { + float mass{}; + if (!select(part, mass)) { + continue; + } + recordTimeStamp(part.timeStamp()); + rowFemtoResultCharm2Prong( + col.globalIndex(), timeStamp, mass, + part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), + part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + } + if constexpr (WithDstar) { + for (auto const& part : dstars) { + float mass{}; + if (!select(part, mass)) { + continue; + } + recordTimeStamp(part.timeStamp()); + const std::array daughterMasses = part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; + // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. + rowFemtoResultCharmDstar( + col.globalIndex(), timeStamp, mass, + part.mDaughD0(daughterMasses), part.pt(), part.eta(), part.phi(), + part.prong0Id(), part.prong1Id(), part.prong2Id(), part.charge(), + part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + } + } + // Retain D0-only/Dstar-only events even when no clean SE pair exists. + // MC fields above are zero for these data processes, as in existing tasks. + if (hasCandidate) { + rowFemtoResultColl(col.globalIndex(), timeStamp, col.posZ(), col.multNtr()); + } + } + + template + void runWithPolicy(FilteredCollisions const& cols, D0Table const& d0Rows, + DstarTable const& dstarRows, Policy const& policy) + { + for (auto const& col : cols) { + auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + // Export before online mixing-bin cuts, so offline mixing can change bins. + if constexpr (WithDstar) { + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + fillTables(col, d0s, dstars); + } else { + fillTables(col, d0s, d0s); + } + const int bin = mixingBin(policy, col); + if (bin < 0 || !std::isfinite(col.magField())) { + registry.fill(HIST("QA/events"), 1); + continue; + } + registry.fill(HIST("QA/events"), 0); + registry.fill(HIST("MixingQA/hSECollisionBins"), bin); + for (auto const& [row1, row2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, col.multNtr()); + } + } + if constexpr (WithDstar) { + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + for (auto const& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, col.multNtr()); + } + } + } + } + if (mixSetting.doMixEvent && mixSetting.mixingDepth > 0) { + doMixedEvent(cols, d0Rows, d0Rows, policy); + if constexpr (WithDstar) { + doMixedEvent(cols, d0Rows, dstarRows, policy); + } + } + } + + template + void run(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0Rows, DstarTable const& dstarRows) + { + validateMlScores(d0Rows); + partitionCharmHadron2Prong.bindTable(d0Rows); + if constexpr (WithDstar) { + validateMlScores(dstarRows); + partitionCharmHadronDstar.bindTable(dstarRows); + } + switch (mixSetting.mixingBinPolicy) { + case aod::femtodreamcollision::kMult: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMult); + break; + case aod::femtodreamcollision::kMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultPercentile); + break; + case aod::femtodreamcollision::kMultMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultMultPercentile); + break; + default: + LOGP(fatal, "Invalid mixing binning policy"); + } + } + + void processD0D0(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0s) + { + run(cols, d0s, d0s); + } + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, "D0D0 data, SE and ME", true); + + void processD0Dstar(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0s, + FilteredCharmCandDstars const& dstars) + { + run(cols, d0s, dstars); + } + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, "D0D0 and D0Dstar data, SE and ME", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} From e09f6c37ed5cbd0541c57e09c30c9378f9ffc9d4 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 21 Sep 2026 14:34:22 +0000 Subject: [PATCH 02/23] Please consider the following formatting changes --- .../TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 6 +++--- PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 5fe9f773306..45607b66828 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -305,7 +305,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0,"D0 only, data", true); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); void processD0Dstar(Collisions::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, @@ -328,7 +328,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml,"D0 with selector ML scores", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); void processD0DstarMl(Collisions::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, @@ -341,7 +341,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl,"D0 and Dstar with selector ML scores", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index f89826fc6f1..3d473d5178a 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -100,12 +100,12 @@ struct HfTaskCharmHadronsCharmFemtoDream { Configurable massMaxD0{"massMaxD0", 2.0f, "Maximum D0 mass"}; Configurable deltaMassMin{"deltaMassMin", 0.139f, "Minimum Dstar-D0 mass difference"}; Configurable deltaMassMax{"deltaMassMax", 0.17f, "Maximum Dstar-D0 mass difference"}; - Configurable daughterMassMin{"daughterMassMin", 1.80f,"Minimum Dstar daughter D0 mass"}; - Configurable daughterMassMax{"daughterMassMax", 1.93f,"Maximum Dstar daughter D0 mass"}; + Configurable daughterMassMin{"daughterMassMin", 1.80f, "Minimum Dstar daughter D0 mass"}; + Configurable daughterMassMax{"daughterMassMax", 1.93f, "Maximum Dstar daughter D0 mass"}; Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; Configurable maxBkgD0{"maxBkgD0", 1.f, "Maximum D0 background score"}; Configurable minPromptD0{"minPromptD0", 0.f, "Minimum D0 prompt score"}; - Configurable maxBkgDstar{"maxBkgDstar", 1.f,"Maximum Dstar background score"}; + Configurable maxBkgDstar{"maxBkgDstar", 1.f, "Maximum Dstar background score"}; Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { From 77adac9b93c2adce34e2bbfb2900034b96c767f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:18:27 +0200 Subject: [PATCH 03/23] Fix formatting issues in taskCharmHadronsCharmFemtoDream.cxx --- PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index 3d473d5178a..d11e035d9be 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -100,12 +100,12 @@ struct HfTaskCharmHadronsCharmFemtoDream { Configurable massMaxD0{"massMaxD0", 2.0f, "Maximum D0 mass"}; Configurable deltaMassMin{"deltaMassMin", 0.139f, "Minimum Dstar-D0 mass difference"}; Configurable deltaMassMax{"deltaMassMax", 0.17f, "Maximum Dstar-D0 mass difference"}; - Configurable daughterMassMin{"daughterMassMin", 1.80f, "Minimum Dstar daughter D0 mass"}; - Configurable daughterMassMax{"daughterMassMax", 1.93f, "Maximum Dstar daughter D0 mass"}; + Configurable daughterMassMin{"daughterMassMin", 1.80f,"Minimum Dstar daughter D0 mass"}; + Configurable daughterMassMax{"daughterMassMax", 1.93f,"Maximum Dstar daughter D0 mass"}; Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; Configurable maxBkgD0{"maxBkgD0", 1.f, "Maximum D0 background score"}; Configurable minPromptD0{"minPromptD0", 0.f, "Minimum D0 prompt score"}; - Configurable maxBkgDstar{"maxBkgDstar", 1.f, "Maximum Dstar background score"}; + Configurable maxBkgDstar{"maxBkgDstar", 1.f,"Maximum Dstar background score"}; Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { @@ -177,7 +177,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { const AxisSpec ptStar{72, ptMinDstar.value, ptMaxDstar.value, "Dstar pT (GeV/c)"}; const AxisSpec mult{mixingBinMult, "NTracksPV"}; const AxisSpec channel{ - kNPairChannels, -0.5, kNPairChannels - 0.5, + static_cast(kNPairChannels), -0.5, static_cast(kNPairChannels) - 0.5, "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; registry.add("SE/D0D0", "Same event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); From a487e7144632428fc373c7ff85812a57798a1d91 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 21 Sep 2026 16:19:03 +0000 Subject: [PATCH 04/23] Please consider the following formatting changes --- PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index d11e035d9be..a822ed7cf0d 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -100,12 +100,12 @@ struct HfTaskCharmHadronsCharmFemtoDream { Configurable massMaxD0{"massMaxD0", 2.0f, "Maximum D0 mass"}; Configurable deltaMassMin{"deltaMassMin", 0.139f, "Minimum Dstar-D0 mass difference"}; Configurable deltaMassMax{"deltaMassMax", 0.17f, "Maximum Dstar-D0 mass difference"}; - Configurable daughterMassMin{"daughterMassMin", 1.80f,"Minimum Dstar daughter D0 mass"}; - Configurable daughterMassMax{"daughterMassMax", 1.93f,"Maximum Dstar daughter D0 mass"}; + Configurable daughterMassMin{"daughterMassMin", 1.80f, "Minimum Dstar daughter D0 mass"}; + Configurable daughterMassMax{"daughterMassMax", 1.93f, "Maximum Dstar daughter D0 mass"}; Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; Configurable maxBkgD0{"maxBkgD0", 1.f, "Maximum D0 background score"}; Configurable minPromptD0{"minPromptD0", 0.f, "Minimum D0 prompt score"}; - Configurable maxBkgDstar{"maxBkgDstar", 1.f,"Maximum Dstar background score"}; + Configurable maxBkgDstar{"maxBkgDstar", 1.f, "Maximum Dstar background score"}; Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { From e75ff5075ef36d40077acb8eebddbd018d8ed7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Mon, 21 Sep 2026 19:07:30 +0200 Subject: [PATCH 05/23] Refactor ML configuration and update includes --- .../producerCharmHadronsCharmFemtoDream.cxx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 45607b66828..54e643c73a9 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -17,7 +17,6 @@ #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/HfMlResponseD0ToKPi.h" #include "PWGHF/Core/HfMlResponseDstarToD0Pi.h" -#include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/Utils/utilsBfieldCCDB.h" @@ -27,19 +26,30 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include #include +#include +#include +#include #include +#include +#include #include #include +#include +#include +#include #include #include #include #include +#include #include #include @@ -56,10 +66,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { // Each species needs its own model, feature order and pT-dependent cuts. struct MlConfig : ConfigurableGroup { std::string prefix; - static constexpr double DefaultCuts[3] = {1., 0., 0.}; + static inline const std::array DefaultCuts{1., 0., 0.}; Configurable applyMlMode{"applyMlMode", FillMlFromSelector, "0: no ML, 1: selector scores, 2: new BDT after selector"}; Configurable> binsPtMl{"binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; - Configurable> cutsMl{"cutsMl", {DefaultCuts, 1, 3}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; + Configurable> cutsMl{"cutsMl", {DefaultCuts.data(), 1, 3}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; Configurable> cutDirMl{"cutDirMl", std::vector{0, 1, 1}, "Reject scores above (0), below (1), or do not cut (2)"}; Configurable nClassesMl{"nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{}, "Ordered input feature names for new BDT"}; @@ -81,7 +91,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { Configurable selectionFlagD0{"selectionFlagD0", 1, "Minimum D0 selector decision"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; - Service ccdb; + Service ccdb{}; o2::hf_evsel::HfEventSelection hfEvSel; OutputObj zorroSummary{"zorroSummary"}; HistogramRegistry registry{"QA", {}, OutputObjHandlingPolicy::AnalysisObject}; From de0030a617d2a763cc201d16e7670b36c9283485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Mon, 21 Sep 2026 19:07:56 +0200 Subject: [PATCH 06/23] Add framework includes to taskCharmHadronsCharmFemtoDream Added several new includes for framework functionalities. --- PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index a822ed7cf0d..d41ae017cd3 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -17,11 +17,18 @@ #include "PWGCF/FemtoDream/Core/femtoDreamMath.h" #include +#include #include +#include +#include #include #include +#include #include #include +#include +#include +#include #include #include From 0cde17af6d5b0e0a7d4c2bf07aa13cbf27a9dd3a Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 21 Sep 2026 17:08:05 +0000 Subject: [PATCH 07/23] Please consider the following formatting changes --- PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 54e643c73a9..8fd8760f693 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -27,7 +27,7 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" -#include +#include "Tools/ML/MlResponse.h" #include #include From 56a7d9d8b10f1161ea8283cfab24779ad99ba284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Mon, 21 Sep 2026 20:34:18 +0200 Subject: [PATCH 08/23] Refactor ML response initialization and includes --- .../producerCharmHadronsCharmFemtoDream.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 8fd8760f693..3d707331fc2 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -26,8 +26,6 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/TrackSelectionTables.h" -#include "Tools/ML/MlResponse.h" #include #include @@ -45,6 +43,7 @@ #include #include #include +#include #include #include @@ -77,7 +76,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{}, "CCDB model paths, one per pT bin"}; Configurable timestampCCDB{"timestampCCDB", -1, "Timestamp used to retrieve models"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Load new BDT from CCDB instead of local files"}; - } mlD0{{}, "mlD0"}, mlDstar{{}, "mlDstar"}; + } mlD0{.prefix = "mlD0"}, mlDstar{.prefix = "mlDstar"}; o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; o2::ccdb::CcdbApi ccdbApi; @@ -159,9 +158,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { void init(InitContext const&) { - if (static_cast(doprocessD0D0) + doprocessD0Dstar + doprocessD0D0Ml + - doprocessD0DstarMl + doprocessD0D0WithFT0C + doprocessD0DstarWithFT0C + - doprocessD0D0MlWithFT0C + doprocessD0DstarMlWithFT0C != + if (static_cast(doprocessD0D0) + static_cast(doprocessD0Dstar) + + static_cast(doprocessD0D0Ml) + static_cast(doprocessD0DstarMl) + + static_cast(doprocessD0D0WithFT0C) + static_cast(doprocessD0DstarWithFT0C) + + static_cast(doprocessD0D0MlWithFT0C) + static_cast(doprocessD0DstarMlWithFT0C) != 1) { LOGP(fatal, "Enable exactly one charm-charm producer process"); } From 63b79b0dfa76deeb7bb127d74febcb414106afec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Mon, 21 Sep 2026 20:34:45 +0200 Subject: [PATCH 09/23] Fix static_cast usage in process check --- PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index d41ae017cd3..cddbee98727 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -162,7 +162,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { void init(InitContext const&) { - if (static_cast(doprocessD0D0) + doprocessD0Dstar != 1) { + if (static_cast(doprocessD0D0) + static_cast(doprocessD0Dstar) != 1) { LOGP(fatal, "Enable exactly one charm-charm analysis process"); } if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > 2 || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || From 74647d52f23cf346934072f609cf84748761713b Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 21 Sep 2026 18:35:19 +0000 Subject: [PATCH 10/23] Please consider the following formatting changes --- PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 3d707331fc2..4fe4a74b215 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -26,6 +26,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Tools/ML/MlResponse.h" #include #include @@ -43,7 +44,6 @@ #include #include #include -#include #include #include From ba9a37df384623df3d19e878bcf9b58a52ef7ae3 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Mon, 21 Sep 2026 19:06:03 +0200 Subject: [PATCH 11/23] PWGHF: Add direct charm femto dependencies --- PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 4fe4a74b215..c5d1a15072d 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include #include From 18e0445f13b50043cb7febfef752a9a1e5d1a980 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Mon, 21 Sep 2026 21:13:53 +0200 Subject: [PATCH 12/23] PWGHF: Address charm femto review comments --- .../producerCharmHadronsCharmFemtoDream.cxx | 293 ++++++----- .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 454 +++++++++++------- 2 files changed, 443 insertions(+), 304 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index c5d1a15072d..84d6b21d897 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -1,6 +1,6 @@ // Copyright 2019-2025 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright +// holders. All rights not expressly granted are reserved. // // This software is distributed under the terms of the GNU General Public // License v3 (GPL Version 3), copied verbatim in the file "COPYING". @@ -26,7 +26,6 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Tools/ML/MlResponse.h" #include #include @@ -63,56 +62,82 @@ struct HfProducerCharmHadronsCharmFemtoDream { FillMlFromSelector = 1, FillMlFromNewBDT = 2 }; + Configurable selectionFlagD0{"selectionFlagD0", 1, + "Minimum D0 selector decision"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", + "CCDB URL"}; + Configurable ccdbPathGrpMag{ + "ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; + // Each species needs its own model, feature order and pT-dependent cuts. struct MlConfig : ConfigurableGroup { std::string prefix; static inline const std::array DefaultCuts{1., 0., 0.}; - Configurable applyMlMode{"applyMlMode", FillMlFromSelector, "0: no ML, 1: selector scores, 2: new BDT after selector"}; - Configurable> binsPtMl{"binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; - Configurable> cutsMl{"cutsMl", {DefaultCuts.data(), 1, 3}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; - Configurable> cutDirMl{"cutDirMl", std::vector{0, 1, 1}, "Reject scores above (0), below (1), or do not cut (2)"}; - Configurable nClassesMl{"nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{}, "Ordered input feature names for new BDT"}; - Configurable> onnxFileNames{"onnxFileNames", std::vector{}, "Model files, one per pT bin"}; - Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{}, "CCDB model paths, one per pT bin"}; - Configurable timestampCCDB{"timestampCCDB", -1, "Timestamp used to retrieve models"}; - Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Load new BDT from CCDB instead of local files"}; + Configurable applyMlMode{ + "applyMlMode", FillMlFromSelector, + "0: no ML, 1: selector scores, 2: new BDT after selector"}; + Configurable> binsPtMl{ + "binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; + Configurable> cutsMl{ + "cutsMl", + {DefaultCuts.data(), 1, 3}, + "New BDT cuts per pT bin: background, prompt, nonprompt"}; + Configurable> cutDirMl{ + "cutDirMl", std::vector{0, 1, 1}, + "Reject scores above (0), below (1), or do not cut (2)"}; + Configurable nClassesMl{ + "nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; + Configurable> namesInputFeatures{ + "namesInputFeatures", std::vector{}, + "Ordered input feature names for new BDT"}; + Configurable> onnxFileNames{ + "onnxFileNames", std::vector{}, + "Model files, one per pT bin"}; + Configurable> modelPathsCCDB{ + "modelPathsCCDB", std::vector{}, + "CCDB model paths, one per pT bin"}; + Configurable timestampCCDB{"timestampCCDB", -1, + "Timestamp used to retrieve models"}; + Configurable loadModelsFromCCDB{ + "loadModelsFromCCDB", false, + "Load new BDT from CCDB instead of local files"}; } mlD0{.prefix = "mlD0"}, mlDstar{.prefix = "mlDstar"}; - o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; - o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; - o2::ccdb::CcdbApi ccdbApi; - Produces collisions; Produces rowMasks; - bool hasD0 = false; - bool hasDstar = false; Produces d0Rows; Produces dstarRows; - Configurable selectionFlagD0{"selectionFlagD0", 1, "Minimum D0 selector decision"}; - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; - Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; + Service ccdb{}; + o2::ccdb::CcdbApi ccdbApi; o2::hf_evsel::HfEventSelection hfEvSel; + o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; + o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; OutputObj zorroSummary{"zorroSummary"}; - HistogramRegistry registry{"QA", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry registry{"registry"}; + + bool hasD0 = false; + bool hasDstar = false; int runNumber = -1; - using Collisions = - soa::Join; + using Collisions = soa::Join; using CollisionsWithFT0C = soa::Join; using D0s = soa::Join; using Dstars = - soa::Join; + soa::Join; using D0sMl = soa::Join; - using DstarsMl = soa::Join; + using DstarsMl = soa::Join; - Filter filterSelectCandidateD0 = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); - Filter filterSelectCandidateDstar = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + Filter filterSelectCandidateD0 = + (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || + aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); + Filter filterSelectCandidateDstar = + aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; template - void initMl(MlConfig const& cfg, Response& response, bool withMl) - { - if (cfg.applyMlMode.value < NoMl || cfg.applyMlMode.value > FillMlFromNewBDT) { + void initMl(MlConfig const &cfg, Response &response, bool withMl) { + if (cfg.applyMlMode.value < NoMl || + cfg.applyMlMode.value > FillMlFromNewBDT) { LOGP(fatal, "{}: invalid applyMlMode", cfg.prefix); } if (cfg.applyMlMode.value != FillMlFromNewBDT) { @@ -121,21 +146,31 @@ struct HfProducerCharmHadronsCharmFemtoDream { if (!withMl) { LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); } - auto const& edges = cfg.binsPtMl.value; + auto const &edges = cfg.binsPtMl.value; if (edges.size() < 2 || - !std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }) || - std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { return a >= b; }) != edges.end()) { - LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", cfg.prefix); + !std::all_of(edges.begin(), edges.end(), + [](double x) { return std::isfinite(x); }) || + std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { + return a >= b; + }) != edges.end()) { + LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", + cfg.prefix); } const auto nBins = edges.size() - 1; if (cfg.nClassesMl.value != 3 || cfg.cutDirMl.value.size() != 3 || cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != 3 || - cfg.onnxFileNames.value.size() != nBins || cfg.namesInputFeatures.value.empty() || - (cfg.loadModelsFromCCDB.value && cfg.modelPathsCCDB.value.size() != nBins)) { - LOGP(fatal, "{}: provide three classes, cuts/models for every pT bin, and input features", cfg.prefix); + cfg.onnxFileNames.value.size() != nBins || + cfg.namesInputFeatures.value.empty() || + (cfg.loadModelsFromCCDB.value && + cfg.modelPathsCCDB.value.size() != nBins)) { + LOGP(fatal, + "{}: provide three classes, cuts/models for every pT bin, and input " + "features", + cfg.prefix); } for (auto direction : cfg.cutDirMl.value) { - if (direction < o2::cuts_ml::CutGreater || direction > o2::cuts_ml::CutNot) { + if (direction < o2::cuts_ml::CutGreater || + direction > o2::cuts_ml::CutNot) { LOGP(fatal, "{}: invalid cutDirMl", cfg.prefix); } } @@ -146,24 +181,31 @@ struct HfProducerCharmHadronsCharmFemtoDream { } } } - response.configure(edges, cfg.cutsMl.value, cfg.cutDirMl.value, cfg.nClassesMl.value); + response.configure(edges, cfg.cutsMl.value, cfg.cutDirMl.value, + cfg.nClassesMl.value); response.cacheInputFeaturesIndices(cfg.namesInputFeatures.value); if (cfg.loadModelsFromCCDB.value) { ccdbApi.init(ccdbUrl.value); - response.setModelPathsCCDB(cfg.onnxFileNames.value, ccdbApi, cfg.modelPathsCCDB.value, cfg.timestampCCDB.value); + response.setModelPathsCCDB(cfg.onnxFileNames.value, ccdbApi, + cfg.modelPathsCCDB.value, + cfg.timestampCCDB.value); } else { response.setModelPathsLocal(cfg.onnxFileNames.value); } response.init(); } - void init(InitContext const&) - { - if (static_cast(doprocessD0D0) + static_cast(doprocessD0Dstar) + - static_cast(doprocessD0D0Ml) + static_cast(doprocessD0DstarMl) + - static_cast(doprocessD0D0WithFT0C) + static_cast(doprocessD0DstarWithFT0C) + - static_cast(doprocessD0D0MlWithFT0C) + static_cast(doprocessD0DstarMlWithFT0C) != - 1) { + void init(InitContext const &) { + const std::array processSwitches{ + static_cast(doprocessD0D0), + static_cast(doprocessD0Dstar), + static_cast(doprocessD0D0Ml), + static_cast(doprocessD0DstarMl), + static_cast(doprocessD0D0WithFT0C), + static_cast(doprocessD0DstarWithFT0C), + static_cast(doprocessD0D0MlWithFT0C), + static_cast(doprocessD0DstarMlWithFT0C)}; + if (std::count(processSwitches.begin(), processSwitches.end(), true) != 1) { LOGP(fatal, "Enable exactly one charm-charm producer process"); } if (selectionFlagD0 < 1) { @@ -172,9 +214,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - const bool withMl = doprocessD0D0Ml || doprocessD0DstarMl || doprocessD0D0MlWithFT0C || doprocessD0DstarMlWithFT0C; + const bool withMl = doprocessD0D0Ml || doprocessD0DstarMl || + doprocessD0D0MlWithFT0C || doprocessD0DstarMlWithFT0C; initMl(mlD0, hfMlResponseD0, withMl); - if (doprocessD0Dstar || doprocessD0DstarMl || doprocessD0DstarWithFT0C || doprocessD0DstarMlWithFT0C) { + if (doprocessD0Dstar || doprocessD0DstarMl || doprocessD0DstarWithFT0C || + doprocessD0DstarMlWithFT0C) { initMl(mlDstar, hfMlResponseDstar, withMl); } hfEvSel.init(registry, &zorroSummary); @@ -184,14 +228,13 @@ struct HfProducerCharmHadronsCharmFemtoDream { kTH1F, {{4, -0.5, 3.5}}); } - template - bool acceptCollision(Collision const& col) - { + template + bool acceptCollision(Collision const &col) { registry.fill(HIST("events"), 0); float cent = -1.f; // No centrality for pp MB. const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask< - true, CentEstimator, - aod::BCsWithTimestamps>(col, cent, ccdb, registry); + true, CentEstimator, aod::BCsWithTimestamps>(col, cent, ccdb, registry); hfEvSel.fillHistograms(col, rejectionMask, cent); if (rejectionMask != 0) { return false; @@ -202,18 +245,17 @@ struct HfProducerCharmHadronsCharmFemtoDream { initCCDB(bc, runNumber, ccdb, ccdbPathGrpMag.value, nullptr, false); // Propagator field is in kG; FemtoDream uses Tesla. const float fieldTesla = - 0.1f * o2::base::Propagator::Instance()->getNominalBz(); - // MultV0M is the common reduced percentile column: FT0C or -1 (no estimator). - collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, - fieldTesla); + 0.1f * o2::base::Propagator::Instance()->getNominalBz(); + // MultV0M is the common reduced percentile column: FT0C or -1 (no + // estimator). + collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, fieldTesla); registry.fill(HIST("events"), 1); // Keep all accepted collisions, including zero/single-candidate events. return true; } template - std::array readScores(Scores const& scores) - { + std::array readScores(Scores const &scores) { if (scores.size() != 3) { LOGP(fatal, "Expected three selector ML scores: background, prompt, nonprompt"); @@ -228,10 +270,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillD0(Collision const& col, Candidates const& candidates) - { - const auto timestamp = col.template bc_as().timestamp(); - for (auto const& cand : candidates) { + void fillD0(Collision const &col, Candidates const &candidates) { + const auto timestamp = + col.template bc_as().timestamp(); + for (auto const &cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); // The OR filter accepts the row if either hypothesis passes. Write only @@ -249,11 +291,15 @@ struct HfProducerCharmHadronsCharmFemtoDream { } if (mlD0.applyMlMode == FillMlFromNewBDT) { // Do not call the ML response with an out-of-range model index. - if (!std::isfinite(cand.pt()) || cand.pt() < mlD0.binsPtMl.value.front() || cand.pt() >= mlD0.binsPtMl.value.back()) { + if (!std::isfinite(cand.pt()) || + cand.pt() < mlD0.binsPtMl.value.front() || + cand.pt() >= mlD0.binsPtMl.value.back()) { continue; } - const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 : -o2::constants::physics::kD0; - auto features = hfMlResponseD0.getInputFeatures(cand, pdgCode); + const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 + : -o2::constants::physics::kD0; + auto features = + hfMlResponseD0.getInputFeatures(cand, pdgCode); std::vector output; if (!hfMlResponseD0.isSelectedMl(features, cand.pt(), output)) { continue; // Reject this hypothesis only. @@ -272,11 +318,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillDstar(Collision const& col, - Candidates const& candidates) - { - const auto timestamp = col.template bc_as().timestamp(); - for (auto const& cand : candidates) { + void fillDstar(Collision const &col, Candidates const &candidates) { + const auto timestamp = + col.template bc_as().timestamp(); + for (auto const &cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); auto soft = cand.template prongPi_as(); @@ -286,7 +331,9 @@ struct HfProducerCharmHadronsCharmFemtoDream { scores = readScores(cand.mlProbDstarToD0Pi()); } if (mlDstar.applyMlMode == FillMlFromNewBDT) { - if (!std::isfinite(cand.pt()) || cand.pt() < mlDstar.binsPtMl.value.front() || cand.pt() >= mlDstar.binsPtMl.value.back()) { + if (!std::isfinite(cand.pt()) || + cand.pt() < mlDstar.binsPtMl.value.front() || + cand.pt() >= mlDstar.binsPtMl.value.back()) { continue; } // Match the unswapped daughter convention used by D+track. @@ -307,103 +354,109 @@ struct HfProducerCharmHadronsCharmFemtoDream { } } - void processD0D0(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + void processD0D0(Collisions::iterator const &col, + aod::BCsWithTimestamps const &, aod::Tracks const &, + soa::Filtered const &d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, + "D0 only, data", true); - void processD0Dstar(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, soa::Filtered const& dstars) - { + void processD0Dstar(Collisions::iterator const &col, + aod::BCsWithTimestamps const &, aod::Tracks const &, + soa::Filtered const &d0s, + soa::Filtered const &dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, + "D0 and Dstar, data", false); - void processD0D0Ml(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + void processD0D0Ml(Collisions::iterator const &col, + aod::BCsWithTimestamps const &, aod::Tracks const &, + soa::Filtered const &d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, + "D0 with selector ML scores", false); - void processD0DstarMl(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, - soa::Filtered const& dstars) - { + void processD0DstarMl(Collisions::iterator const &col, + aod::BCsWithTimestamps const &, aod::Tracks const &, + soa::Filtered const &d0s, + soa::Filtered const &dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, + "D0 and Dstar with selector ML scores", false); - void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + void processD0D0WithFT0C(CollisionsWithFT0C::iterator const &col, + aod::BCsWithTimestamps const &, aod::Tracks const &, + soa::Filtered const &d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, "D0 only, data with FT0C centrality", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, + "D0 only, data with FT0C centrality", false); - void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, soa::Filtered const& dstars) - { + void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const &col, + aod::BCsWithTimestamps const &, + aod::Tracks const &, + soa::Filtered const &d0s, + soa::Filtered const &dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, "D0 and Dstar, data with FT0C centrality", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, + "D0 and Dstar, data with FT0C centrality", false); - void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const &col, + aod::BCsWithTimestamps const &, + aod::Tracks const &, + soa::Filtered const &d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, "D0 with selector ML scores and FT0C centrality", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, + "D0 with selector ML scores and FT0C centrality", false); - void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, - soa::Filtered const& dstars) - { + void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const &col, + aod::BCsWithTimestamps const &, + aod::Tracks const &, + soa::Filtered const &d0s, + soa::Filtered const &dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMlWithFT0C, "D0 and Dstar with selector ML scores and FT0C centrality", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, + processD0DstarMlWithFT0C, + "D0 and Dstar with selector ML scores and FT0C centrality", + false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ +WorkflowSpec defineDataProcessing(ConfigContext const &cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index cddbee98727..c8cf10f3d77 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -1,6 +1,6 @@ // Copyright 2019-2025 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright +// holders. All rights not expressly granted are reserved. // // This software is distributed under the terms of the GNU General Public // License v3 (GPL Version 3), copied verbatim in the file "COPYING". @@ -45,147 +45,153 @@ using namespace o2::analysis::femtoDream; struct HfTaskCharmHadronsCharmFemtoDream { // Like/unlike sign refers to charm flavour; D0 is electrically neutral. - // Keep charge-conjugate channels separate so they can also be combined offline. + // Keep charge-conjugate channels separate so they can also be combined + // offline. enum PairChannel : int { - kD0D0LikeSign = 0, - kD0BarD0BarLikeSign = 1, - kD0D0BarUnlikeSign = 2, - kD0DstarPlusLikeSign = 3, - kD0BarDstarMinusLikeSign = 4, - kD0DstarMinusUnlikeSign = 5, - kD0BarDstarPlusUnlikeSign = 6, - kNPairChannels = 7 + D0D0LikeSign = 0, + D0barD0barLikeSign = 1, + D0D0barUnlikeSign = 2, + D0DstarPlusLikeSign = 3, + D0barDstarMinusLikeSign = 4, + D0DstarMinusUnlikeSign = 5, + D0barDstarPlusUnlikeSign = 6, + NPairChannels = 7 }; - template - static bool sharesDaughter(D0Row const& a, OtherRow const& b) - { - for (auto id : std::array{a.prong0Id(), a.prong1Id()}) { - if (id < 0) { - continue; - } - if (id == b.prong0Id() || id == b.prong1Id()) { - return true; - } - if constexpr (IsDstar) { - if (id == b.prong2Id()) { - return true; - } - } - } - return false; - } - - // a is D0; b is either D0 or Dstar. D0 charge means flavour, not electric - // charge. - template - static PairChannel pairChannel(D0Row const& a, OtherRow const& b) - { - if constexpr (!IsDstar) { - return a.charge() != b.charge() ? kD0D0BarUnlikeSign : (a.charge() > 0 ? kD0D0LikeSign : kD0BarD0BarLikeSign); - } - return a.charge() > 0 ? (b.charge() > 0 ? kD0DstarPlusLikeSign : kD0DstarMinusUnlikeSign) - : (b.charge() < 0 ? kD0BarDstarMinusLikeSign : kD0BarDstarPlusUnlikeSign); - } - - // Canonical ordering keeps mass/pt axes independent of event ordering for D0D0. - template - static bool reverseD0Order(FirstRow const& a, SecondRow const& b) - { - if (a.charge() != b.charge()) { - return a.charge() < b.charge(); // D0 first for D0-D0bar - } - return a.pt() < b.pt(); // leading pT first for equal flavours - } - Configurable ptMinD0{"ptMinD0", 0.f, "Minimum D0 pT"}; Configurable ptMaxD0{"ptMaxD0", 36.f, "Maximum D0 pT"}; Configurable ptMinDstar{"ptMinDstar", 0.f, "Minimum Dstar pT"}; Configurable ptMaxDstar{"ptMaxDstar", 36.f, "Maximum Dstar pT"}; Configurable etaMax{"etaMax", 0.8f, "Maximum absolute candidate eta"}; - Configurable massMinD0{"massMinD0", 1.7f, "Minimum D0 mass (keep sidebands)"}; + Configurable massMinD0{"massMinD0", 1.7f, + "Minimum D0 mass (keep sidebands)"}; Configurable massMaxD0{"massMaxD0", 2.0f, "Maximum D0 mass"}; - Configurable deltaMassMin{"deltaMassMin", 0.139f, "Minimum Dstar-D0 mass difference"}; - Configurable deltaMassMax{"deltaMassMax", 0.17f, "Maximum Dstar-D0 mass difference"}; - Configurable daughterMassMin{"daughterMassMin", 1.80f, "Minimum Dstar daughter D0 mass"}; - Configurable daughterMassMax{"daughterMassMax", 1.93f, "Maximum Dstar daughter D0 mass"}; - Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; + Configurable deltaMassMin{"deltaMassMin", 0.139f, + "Minimum Dstar-D0 mass difference"}; + Configurable deltaMassMax{"deltaMassMax", 0.17f, + "Maximum Dstar-D0 mass difference"}; + Configurable daughterMassMin{"daughterMassMin", 1.80f, + "Minimum Dstar daughter D0 mass"}; + Configurable daughterMassMax{"daughterMassMax", 1.93f, + "Maximum Dstar daughter D0 mass"}; + Configurable useMl{"useMl", false, + "Require valid ML scores and apply score cuts"}; Configurable maxBkgD0{"maxBkgD0", 1.f, "Maximum D0 background score"}; - Configurable minPromptD0{"minPromptD0", 0.f, "Minimum D0 prompt score"}; - Configurable maxBkgDstar{"maxBkgDstar", 1.f, "Maximum Dstar background score"}; - Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; - Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; + Configurable minPromptD0{"minPromptD0", 0.f, + "Minimum D0 prompt score"}; + Configurable maxBkgDstar{"maxBkgDstar", 1.f, + "Maximum Dstar background score"}; + Configurable minPromptDstar{"minPromptDstar", 0.f, + "Minimum Dstar prompt score"}; + Configurable charmHadCandSel{ + "charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { std::string prefix = "eventSel"; - Configurable useCentrality{"useCentrality", false, "Apply percentile selection (requires a centrality-enabled producer)"}; + Configurable useCentrality{ + "useCentrality", false, + "Apply percentile selection (requires a centrality-enabled producer)"}; Configurable multMin{"multMin", 0, "Minimum multiplicity (MultNtr)"}; - Configurable multMax{"multMax", 99999, "Maximum multiplicity (MultNtr)"}; - Configurable multPercentileMin{"multPercentileMin", 0.f, "Minimum multiplicity percentile"}; - Configurable multPercentileMax{"multPercentileMax", 100.f, "Maximum multiplicity percentile"}; + Configurable multMax{"multMax", 99999, + "Maximum multiplicity (MultNtr)"}; + Configurable multPercentileMin{"multPercentileMin", 0.f, + "Minimum multiplicity percentile"}; + Configurable multPercentileMax{"multPercentileMax", 100.f, + "Maximum multiplicity percentile"}; } eventSel; struct : ConfigurableGroup { Configurable doMixEvent{"doMixEvent", true, "Enable mixed events"}; - Configurable mixingBinPolicy{"mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; - Configurable mixingDepth{"mixingDepth", 5, "Number of neighbours for O2 event mixing"}; + Configurable mixingBinPolicy{ + "mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; + Configurable mixingDepth{"mixingDepth", 5, + "Number of neighbours for O2 event mixing"}; } mixSetting; - ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; - ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; - ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + ConfigurableAxis mixingBinMult{"mixingBinMult", + {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, + "Mixing bins - multiplicity"}; + ConfigurableAxis mixingBinMultPercentile{ + "mixingBinMultPercentile", + {VARIABLE_WIDTH, 0.f, 100.f}, + "Mixing bins - multiplicity percentile"}; + ConfigurableAxis mixingBinVztx{"mixingBinVztx", + {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, + "Mixing bins - z-vertex"}; + ColumnBinningPolicy + colBinningMult{{mixingBinVztx, mixingBinMult}, true}; + ColumnBinningPolicy + colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; + ColumnBinningPolicy + colBinningMultMultPercentile{ + {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; - using FilteredCollisions = soa::Filtered>; + using FilteredCollisions = + soa::Filtered>; using FilteredCharmCand2Prongs = soa::Filtered; using FilteredCharmCandDstars = soa::Filtered; - Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= eventSel.multMin && aod::femtodreamcollision::multNtr <= eventSel.multMax; - Filter eventMultiplicityPercentile = ifnode(eventSel.useCentrality, aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, Node{LiteralNode{true}}); + Filter eventMultiplicity = + aod::femtodreamcollision::multNtr >= eventSel.multMin && + aod::femtodreamcollision::multNtr <= eventSel.multMax; + Filter eventMultiplicityPercentile = ifnode( + eventSel.useCentrality, + aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && + aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, + Node{LiteralNode{true}}); Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmHadCandSel; - Partition partitionCharmHadron2Prong = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, Node{LiteralNode{true}}); - Partition partitionCharmHadronDstar = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgDstar && aod::fdhf::bdtPrompt >= minPromptDstar, Node{LiteralNode{true}}); + Partition partitionCharmHadron2Prong = ifnode( + useMl, + aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, + Node{LiteralNode{true}}); + Partition partitionCharmHadronDstar = + ifnode(useMl, + aod::fdhf::bdtBkg <= maxBkgDstar && + aod::fdhf::bdtPrompt >= minPromptDstar, + Node{LiteralNode{true}}); // Full-table process inputs do not automatically register grouping caches. // Register both keys explicitly for the sliceByCached calls below. - Preslice perCollisionD0 = aod::femtodreamparticle::fdCollisionId; - Preslice perCollisionDstar = aod::femtodreamparticle::fdCollisionId; + Preslice perCollisionD0 = + aod::femtodreamparticle::fdCollisionId; + Preslice perCollisionDstar = + aod::femtodreamparticle::fdCollisionId; SliceCache cache; - HistogramRegistry registry{ - "Results", - {}, - OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry registry{"registry"}; Produces rowFemtoResultCharm2Prong; Produces rowFemtoResultCharmDstar; Produces rowFemtoResultColl; - void init(InitContext const&) - { - if (static_cast(doprocessD0D0) + static_cast(doprocessD0Dstar) != 1) { + void init(InitContext const &) { + if (doprocessD0D0 == doprocessD0Dstar) { LOGP(fatal, "Enable exactly one charm-charm analysis process"); } - if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > 2 || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || + if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || + mixSetting.mixingBinPolicy > 2 || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || ptMinDstar < 0 || ptMinDstar >= ptMaxDstar || etaMax <= 0 || massMinD0 >= massMaxD0 || deltaMassMin >= deltaMassMax || daughterMassMin >= daughterMassMax || charmHadCandSel < 1 || - eventSel.multMin > eventSel.multMax || eventSel.multPercentileMin > eventSel.multPercentileMax) { + eventSel.multMin > eventSel.multMax || + eventSel.multPercentileMin > eventSel.multPercentileMax) { LOGP(fatal, "Invalid charm-charm selection or mixing configuration"); } colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMultMultPercentile = { + {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; const AxisSpec massD0{300, massMinD0.value, massMaxD0.value, "M(Kpi) (GeV/c2)"}; const AxisSpec deltaMass{310, deltaMassMin.value, deltaMassMax.value, "Delta M (GeV/c2)"}; const AxisSpec pt{72, ptMinD0.value, ptMaxD0.value, "D0 pT (GeV/c)"}; - const AxisSpec ptStar{72, ptMinDstar.value, ptMaxDstar.value, "Dstar pT (GeV/c)"}; + const AxisSpec ptStar{72, ptMinDstar.value, ptMaxDstar.value, + "Dstar pT (GeV/c)"}; const AxisSpec mult{mixingBinMult, "NTracksPV"}; - const AxisSpec channel{ - static_cast(kNPairChannels), -0.5, static_cast(kNPairChannels) - 0.5, - "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; + const AxisSpec channel{static_cast(NPairChannels), -0.5, + static_cast(NPairChannels) - 0.5, + "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ " + "LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; registry.add("SE/D0D0", "Same event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); registry.add("ME/D0D0", "Mixed event", kTHnSparseF, @@ -196,32 +202,76 @@ struct HfTaskCharmHadronsCharmFemtoDream { {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); registry.add("QA/events", ";0=in mixing range,1=outside;events", kTH1F, {{2, -0.5, 1.5}}); - registry.add("MixingQA/hSECollisionBins", ";mixing bin;events", kTH1F, {{1000, -0.5, 999.5}}); - registry.add("MixingQA/hMECollisionBinsD0D0", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); - registry.add("MixingQA/hMECollisionBinsD0Dstar", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hSECollisionBins", ";mixing bin;events", kTH1F, + {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0D0", ";mixing bin;event pairs", + kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0Dstar", ";mixing bin;event pairs", + kTH1F, {{1000, -0.5, 999.5}}); registry.add("QA/sharedDaughters", ";0=D0D0,1=D0Dstar;rejected pairs", kTH1F, {{2, -0.5, 1.5}}); registry.add("QA/d0MassPt", ";M(Kpi);pT", kTH2F, {massD0, pt}); registry.add("QA/dstarMassPt", ";Delta M;pT", kTH2F, {deltaMass, ptStar}); } + template + static bool sharesDaughter(D0Row const &a, OtherRow const &b) { + for (auto id : std::array{a.prong0Id(), a.prong1Id()}) { + if (id < 0) { + continue; + } + if (id == b.prong0Id() || id == b.prong1Id()) { + return true; + } + if constexpr (IsDstar) { + if (id == b.prong2Id()) { + return true; + } + } + } + return false; + } + + // D0 charge denotes flavour, not electric charge. + template + static PairChannel pairChannel(D0Row const &d0, OtherRow const &other) { + if constexpr (!IsDstar) { + return d0.charge() != other.charge() + ? D0D0barUnlikeSign + : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); + } + return d0.charge() > 0 ? (other.charge() > 0 ? D0DstarPlusLikeSign + : D0DstarMinusUnlikeSign) + : (other.charge() < 0 ? D0barDstarMinusLikeSign + : D0barDstarPlusUnlikeSign); + } + + // Canonical ordering keeps mass/pt axes independent of event ordering for + // D0D0. + template + static bool reverseD0Order(FirstRow const &first, SecondRow const &second) { + if (first.charge() != second.charge()) { + return first.charge() < second.charge(); // D0 first for D0-D0bar + } + return first.pt() < second.pt(); // leading pT first for equal flavours + } + template - void validateMlScores(Candidates const& candidates) - { + void validateMlScores(Candidates const &candidates) { if (!useMl) { return; } - for (auto const& row : candidates) { + for (auto const &row : candidates) { if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { - LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML producer process"); + LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML " + "producer process"); } } } template - bool select(Row const& row, float& mass) - { + bool select(Row const &row, float &mass) { if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || std::abs(row.eta()) >= etaMax || @@ -230,8 +280,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { return false; } const std::array masses = - row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} - : std::array{MassKPlus, MassPiPlus}; + row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} + : std::array{MassKPlus, MassPiPlus}; if constexpr (IsDstar) { const float daughterMass = row.mDaughD0(masses); if (!std::isfinite(daughterMass) || daughterMass < daughterMassMin || @@ -260,9 +310,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void fillPair(FirstRow const& first, SecondRow const& second, - float firstMass, float secondMass, float mult) - { + void fillPair(FirstRow const &first, SecondRow const &second, float firstMass, + float secondMass, float mult) { if constexpr (!Mixed) { if (sharesDaughter(first, second)) { registry.fill(HIST("QA/sharedDaughters"), IsDstar ? 1 : 0); @@ -276,71 +325,91 @@ struct HfTaskCharmHadronsCharmFemtoDream { const float pt2 = reverse ? first.pt() : second.pt(); // Read kinematics directly from the candidate rows. Only D0D0 can reverse; // its two nominal parent masses are equal. Never use Dstar delta mass here. - const float kstar = reverse - ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) - : FemtoDreamMath::getkstar(first, MassD0, second, IsDstar ? MassDStar : MassD0); + const float kstar = + reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) + : FemtoDreamMath::getkstar(first, MassD0, second, + IsDstar ? MassDStar : MassD0); if (!std::isfinite(kstar)) { return; } const int channel = pairChannel(first, second); if constexpr (IsDstar && Mixed) { - registry.fill(HIST("ME/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + registry.fill(HIST("ME/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, + channel); } else if constexpr (IsDstar) { - registry.fill(HIST("SE/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + registry.fill(HIST("SE/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, + channel); } else if constexpr (Mixed) { - registry.fill(HIST("ME/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + registry.fill(HIST("ME/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, + channel); } else { - registry.fill(HIST("SE/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + registry.fill(HIST("SE/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, + channel); } } template - int mixingBin(Policy const& policy, Collision col) - { + int mixingBin(Policy const &policy, Collision col) { return policy.getBin(policy.getBinningValues(col)); } - template - void doMixedEvent(FilteredCollisions const& cols, D0Table const& d0Rows, - OtherTable const& otherRows, Policy const& policy) - { - Partition partitionMaskedCol1 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + template + void doMixedEvent(FilteredCollisions const &cols, D0Table const &d0Rows, + OtherTable const &otherRows, Policy const &policy) { + Partition partitionMaskedCol1 = + (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. - Partition partitionMaskedCol2 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; - Partition partitionMaskedColDstar = (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; + Partition partitionMaskedCol2 = + (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + Partition partitionMaskedColDstar = + (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; partitionMaskedCol1.bindTable(cols); if constexpr (IsDstar) { partitionMaskedColDstar.bindTable(cols); } else { partitionMaskedCol2.bindTable(cols); } - auto const& secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered : *partitionMaskedCol2.mFiltered; - for (auto const& [collision1, collision2] : combinations(soa::CombinationsBlockFullIndexPolicy(policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, secondCollisions))) { + auto const &secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered + : *partitionMaskedCol2.mFiltered; + for (auto const &[collision1, collision2] : + combinations(soa::CombinationsBlockFullIndexPolicy( + policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, + secondCollisions))) { if (collision1.globalIndex() == collision2.globalIndex()) { continue; } if constexpr (!IsDstar) { - // Full policy returns both orientations for identical inputs. Count once. + // Full policy returns both orientations for identical inputs. Count + // once. if (collision1.globalIndex() > collision2.globalIndex()) { continue; } } - if (!std::isfinite(collision1.magField()) || collision1.magField() != collision2.magField()) { + if (!std::isfinite(collision1.magField()) || + collision1.magField() != collision2.magField()) { continue; } if constexpr (IsDstar) { - registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), mixingBin(policy, collision1)); + registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), + mixingBin(policy, collision1)); } else { - registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), mixingBin(policy, collision1)); + registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), + mixingBin(policy, collision1)); } - auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); - auto second = otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision2.globalIndex(), cache); + auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, + collision1.globalIndex(), cache); + auto second = + otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, + collision2.globalIndex(), cache); // Species roles remain fixed; the full policy supplies the reverse event // orientation when eligible. Do not add a second manual reverse loop. - for (auto const& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(first, second))) { + for (auto const &[row1, row2] : + combinations(soa::CombinationsFullIndexPolicy(first, second))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { - fillPair(row1, row2, mass1, mass2, collision1.multNtr()); + fillPair(row1, row2, mass1, mass2, + collision1.multNtr()); } } } @@ -348,9 +417,10 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Write candidates once per collision, independently of SE/ME pair counts. // Reuse the same output tables and field conventions as the track/V0 tasks. - template - void fillTables(CollType const& col, D0Slice const& d0s, DstarSlice const& dstars) - { + template + void fillTables(CollType const &col, D0Slice const &d0s, + DstarSlice const &dstars) { int64_t timeStamp = -1; bool hasCandidate = false; auto recordTimeStamp = [&](int64_t value) { @@ -360,49 +430,55 @@ struct HfTaskCharmHadronsCharmFemtoDream { timeStamp = value; hasCandidate = true; }; - for (auto const& part : d0s) { + for (auto const &part : d0s) { float mass{}; if (!select(part, mass)) { continue; } recordTimeStamp(part.timeStamp()); - rowFemtoResultCharm2Prong( - col.globalIndex(), timeStamp, mass, - part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), - part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + rowFemtoResultCharm2Prong(col.globalIndex(), timeStamp, mass, part.pt(), + part.eta(), part.phi(), part.prong0Id(), + part.prong1Id(), part.charge(), part.bdtBkg(), + part.bdtPrompt(), part.bdtFD(), 0, 0); } if constexpr (WithDstar) { - for (auto const& part : dstars) { + for (auto const &part : dstars) { float mass{}; if (!select(part, mass)) { continue; } recordTimeStamp(part.timeStamp()); - const std::array daughterMasses = part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; + const std::array daughterMasses = + part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} + : std::array{MassKPlus, MassPiPlus}; // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. rowFemtoResultCharmDstar( - col.globalIndex(), timeStamp, mass, - part.mDaughD0(daughterMasses), part.pt(), part.eta(), part.phi(), - part.prong0Id(), part.prong1Id(), part.prong2Id(), part.charge(), - part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), + part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), + part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), + part.bdtFD(), 0, 0); } } // Retain D0-only/Dstar-only events even when no clean SE pair exists. // MC fields above are zero for these data processes, as in existing tasks. if (hasCandidate) { - rowFemtoResultColl(col.globalIndex(), timeStamp, col.posZ(), col.multNtr()); + rowFemtoResultColl(col.globalIndex(), timeStamp, col.posZ(), + col.multNtr()); } } - template - void runWithPolicy(FilteredCollisions const& cols, D0Table const& d0Rows, - DstarTable const& dstarRows, Policy const& policy) - { - for (auto const& col : cols) { - auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); - // Export before online mixing-bin cuts, so offline mixing can change bins. + template + void runWithPolicy(FilteredCollisions const &cols, D0Table const &d0Rows, + DstarTable const &dstarRows, Policy const &policy) { + for (auto const &col : cols) { + auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, + col.globalIndex(), cache); + // Export before online mixing-bin cuts, so offline mixing can change + // bins. if constexpr (WithDstar) { - auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + auto dstars = dstarRows.sliceByCached( + aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); fillTables(col, d0s, dstars); } else { fillTables(col, d0s, d0s); @@ -414,15 +490,18 @@ struct HfTaskCharmHadronsCharmFemtoDream { } registry.fill(HIST("QA/events"), 0); registry.fill(HIST("MixingQA/hSECollisionBins"), bin); - for (auto const& [row1, row2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { + for (auto const &[row1, row2] : + combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { fillPair(row1, row2, mass1, mass2, col.multNtr()); } } if constexpr (WithDstar) { - auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); - for (auto const& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { + auto dstars = dstarRows.sliceByCached( + aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + for (auto const &[row1, row2] : + combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { fillPair(row1, row2, mass1, mass2, col.multNtr()); @@ -439,8 +518,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void run(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0Rows, DstarTable const& dstarRows) - { + void run(FilteredCollisions const &cols, + FilteredCharmCand2Prongs const &d0Rows, + DstarTable const &dstarRows) { validateMlScores(d0Rows); partitionCharmHadron2Prong.bindTable(d0Rows); if constexpr (WithDstar) { @@ -448,36 +528,42 @@ struct HfTaskCharmHadronsCharmFemtoDream { partitionCharmHadronDstar.bindTable(dstarRows); } switch (mixSetting.mixingBinPolicy) { - case aod::femtodreamcollision::kMult: - runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMult); - break; - case aod::femtodreamcollision::kMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultPercentile); - break; - case aod::femtodreamcollision::kMultMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultMultPercentile); - break; - default: - LOGP(fatal, "Invalid mixing binning policy"); + case aod::femtodreamcollision::kMult: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, colBinningMult); + break; + case aod::femtodreamcollision::kMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, + colBinningMultPercentile); + break; + case aod::femtodreamcollision::kMultMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, + colBinningMultMultPercentile); + break; + default: + LOGP(fatal, "Invalid mixing binning policy"); } } - void processD0D0(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0s) - { + void processD0D0(FilteredCollisions const &cols, + FilteredCharmCand2Prongs const &d0s) { run(cols, d0s, d0s); } - PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, "D0D0 data, SE and ME", true); + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, + "D0D0 data, SE and ME", true); - void processD0Dstar(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0s, - FilteredCharmCandDstars const& dstars) - { + void processD0Dstar(FilteredCollisions const &cols, + FilteredCharmCand2Prongs const &d0s, + FilteredCharmCandDstars const &dstars) { run(cols, d0s, dstars); } - PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, "D0D0 and D0Dstar data, SE and ME", false); + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, + "D0D0 and D0Dstar data, SE and ME", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ +WorkflowSpec defineDataProcessing(ConfigContext const &cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } From 81b6a90f013aa304689b62f884b07eeea57f917f Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 21 Sep 2026 19:15:36 +0000 Subject: [PATCH 13/23] Please consider the following formatting changes --- .../producerCharmHadronsCharmFemtoDream.cxx | 171 +++++++------- .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 221 ++++++++++-------- 2 files changed, 212 insertions(+), 180 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 84d6b21d897..848a74fcfca 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -26,6 +26,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Tools/ML/MlResponse.h" #include #include @@ -43,7 +44,6 @@ #include #include #include -#include #include #include @@ -67,40 +67,40 @@ struct HfProducerCharmHadronsCharmFemtoDream { Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; Configurable ccdbPathGrpMag{ - "ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; + "ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; // Each species needs its own model, feature order and pT-dependent cuts. struct MlConfig : ConfigurableGroup { std::string prefix; static inline const std::array DefaultCuts{1., 0., 0.}; Configurable applyMlMode{ - "applyMlMode", FillMlFromSelector, - "0: no ML, 1: selector scores, 2: new BDT after selector"}; + "applyMlMode", FillMlFromSelector, + "0: no ML, 1: selector scores, 2: new BDT after selector"}; Configurable> binsPtMl{ - "binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; + "binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; Configurable> cutsMl{ - "cutsMl", - {DefaultCuts.data(), 1, 3}, - "New BDT cuts per pT bin: background, prompt, nonprompt"}; + "cutsMl", + {DefaultCuts.data(), 1, 3}, + "New BDT cuts per pT bin: background, prompt, nonprompt"}; Configurable> cutDirMl{ - "cutDirMl", std::vector{0, 1, 1}, - "Reject scores above (0), below (1), or do not cut (2)"}; + "cutDirMl", std::vector{0, 1, 1}, + "Reject scores above (0), below (1), or do not cut (2)"}; Configurable nClassesMl{ - "nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; + "nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; Configurable> namesInputFeatures{ - "namesInputFeatures", std::vector{}, - "Ordered input feature names for new BDT"}; + "namesInputFeatures", std::vector{}, + "Ordered input feature names for new BDT"}; Configurable> onnxFileNames{ - "onnxFileNames", std::vector{}, - "Model files, one per pT bin"}; + "onnxFileNames", std::vector{}, + "Model files, one per pT bin"}; Configurable> modelPathsCCDB{ - "modelPathsCCDB", std::vector{}, - "CCDB model paths, one per pT bin"}; + "modelPathsCCDB", std::vector{}, + "CCDB model paths, one per pT bin"}; Configurable timestampCCDB{"timestampCCDB", -1, "Timestamp used to retrieve models"}; Configurable loadModelsFromCCDB{ - "loadModelsFromCCDB", false, - "Load new BDT from CCDB instead of local files"}; + "loadModelsFromCCDB", false, + "Load new BDT from CCDB instead of local files"}; } mlD0{.prefix = "mlD0"}, mlDstar{.prefix = "mlDstar"}; Produces collisions; Produces rowMasks; @@ -122,20 +122,21 @@ struct HfProducerCharmHadronsCharmFemtoDream { using CollisionsWithFT0C = soa::Join; using D0s = soa::Join; using Dstars = - soa::Join; + soa::Join; using D0sMl = soa::Join; using DstarsMl = soa::Join; Filter filterSelectCandidateD0 = - (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || - aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); + (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || + aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); Filter filterSelectCandidateDstar = - aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; template - void initMl(MlConfig const &cfg, Response &response, bool withMl) { + void initMl(MlConfig const& cfg, Response& response, bool withMl) + { if (cfg.applyMlMode.value < NoMl || cfg.applyMlMode.value > FillMlFromNewBDT) { LOGP(fatal, "{}: invalid applyMlMode", cfg.prefix); @@ -146,7 +147,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { if (!withMl) { LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); } - auto const &edges = cfg.binsPtMl.value; + auto const& edges = cfg.binsPtMl.value; if (edges.size() < 2 || !std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }) || @@ -195,16 +196,17 @@ struct HfProducerCharmHadronsCharmFemtoDream { response.init(); } - void init(InitContext const &) { + void init(InitContext const&) + { const std::array processSwitches{ - static_cast(doprocessD0D0), - static_cast(doprocessD0Dstar), - static_cast(doprocessD0D0Ml), - static_cast(doprocessD0DstarMl), - static_cast(doprocessD0D0WithFT0C), - static_cast(doprocessD0DstarWithFT0C), - static_cast(doprocessD0D0MlWithFT0C), - static_cast(doprocessD0DstarMlWithFT0C)}; + static_cast(doprocessD0D0), + static_cast(doprocessD0Dstar), + static_cast(doprocessD0D0Ml), + static_cast(doprocessD0DstarMl), + static_cast(doprocessD0D0WithFT0C), + static_cast(doprocessD0DstarWithFT0C), + static_cast(doprocessD0D0MlWithFT0C), + static_cast(doprocessD0DstarMlWithFT0C)}; if (std::count(processSwitches.begin(), processSwitches.end(), true) != 1) { LOGP(fatal, "Enable exactly one charm-charm producer process"); } @@ -230,11 +232,12 @@ struct HfProducerCharmHadronsCharmFemtoDream { template - bool acceptCollision(Collision const &col) { + bool acceptCollision(Collision const& col) + { registry.fill(HIST("events"), 0); float cent = -1.f; // No centrality for pp MB. const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask< - true, CentEstimator, aod::BCsWithTimestamps>(col, cent, ccdb, registry); + true, CentEstimator, aod::BCsWithTimestamps>(col, cent, ccdb, registry); hfEvSel.fillHistograms(col, rejectionMask, cent); if (rejectionMask != 0) { return false; @@ -245,7 +248,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { initCCDB(bc, runNumber, ccdb, ccdbPathGrpMag.value, nullptr, false); // Propagator field is in kG; FemtoDream uses Tesla. const float fieldTesla = - 0.1f * o2::base::Propagator::Instance()->getNominalBz(); + 0.1f * o2::base::Propagator::Instance()->getNominalBz(); // MultV0M is the common reduced percentile column: FT0C or -1 (no // estimator). collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, fieldTesla); @@ -255,7 +258,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - std::array readScores(Scores const &scores) { + std::array readScores(Scores const& scores) + { if (scores.size() != 3) { LOGP(fatal, "Expected three selector ML scores: background, prompt, nonprompt"); @@ -270,10 +274,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillD0(Collision const &col, Candidates const &candidates) { + void fillD0(Collision const& col, Candidates const& candidates) + { const auto timestamp = - col.template bc_as().timestamp(); - for (auto const &cand : candidates) { + col.template bc_as().timestamp(); + for (auto const& cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); // The OR filter accepts the row if either hypothesis passes. Write only @@ -299,7 +304,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 : -o2::constants::physics::kD0; auto features = - hfMlResponseD0.getInputFeatures(cand, pdgCode); + hfMlResponseD0.getInputFeatures(cand, pdgCode); std::vector output; if (!hfMlResponseD0.isSelectedMl(features, cand.pt(), output)) { continue; // Reject this hypothesis only. @@ -318,10 +323,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillDstar(Collision const &col, Candidates const &candidates) { + void fillDstar(Collision const& col, Candidates const& candidates) + { const auto timestamp = - col.template bc_as().timestamp(); - for (auto const &cand : candidates) { + col.template bc_as().timestamp(); + for (auto const& cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); auto soft = cand.template prongPi_as(); @@ -354,9 +360,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } } - void processD0D0(Collisions::iterator const &col, - aod::BCsWithTimestamps const &, aod::Tracks const &, - soa::Filtered const &d0s) { + void processD0D0(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -365,10 +372,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); - void processD0Dstar(Collisions::iterator const &col, - aod::BCsWithTimestamps const &, aod::Tracks const &, - soa::Filtered const &d0s, - soa::Filtered const &dstars) { + void processD0Dstar(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -378,9 +386,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); - void processD0D0Ml(Collisions::iterator const &col, - aod::BCsWithTimestamps const &, aod::Tracks const &, - soa::Filtered const &d0s) { + void processD0D0Ml(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -389,10 +398,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); - void processD0DstarMl(Collisions::iterator const &col, - aod::BCsWithTimestamps const &, aod::Tracks const &, - soa::Filtered const &d0s, - soa::Filtered const &dstars) { + void processD0DstarMl(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -402,9 +412,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); - void processD0D0WithFT0C(CollisionsWithFT0C::iterator const &col, - aod::BCsWithTimestamps const &, aod::Tracks const &, - soa::Filtered const &d0s) { + void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, aod::Tracks const&, + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -413,11 +424,12 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, "D0 only, data with FT0C centrality", false); - void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const &col, - aod::BCsWithTimestamps const &, - aod::Tracks const &, - soa::Filtered const &d0s, - soa::Filtered const &dstars) { + void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -427,10 +439,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, "D0 and Dstar, data with FT0C centrality", false); - void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const &col, - aod::BCsWithTimestamps const &, - aod::Tracks const &, - soa::Filtered const &d0s) { + void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -439,11 +452,12 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, "D0 with selector ML scores and FT0C centrality", false); - void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const &col, - aod::BCsWithTimestamps const &, - aod::Tracks const &, - soa::Filtered const &d0s, - soa::Filtered const &dstars) { + void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -456,7 +470,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { false); }; -WorkflowSpec defineDataProcessing(ConfigContext const &cfgc) { +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index c8cf10f3d77..6f01cc5678c 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -84,12 +84,12 @@ struct HfTaskCharmHadronsCharmFemtoDream { Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; Configurable charmHadCandSel{ - "charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; + "charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { std::string prefix = "eventSel"; Configurable useCentrality{ - "useCentrality", false, - "Apply percentile selection (requires a centrality-enabled producer)"}; + "useCentrality", false, + "Apply percentile selection (requires a centrality-enabled producer)"}; Configurable multMin{"multMin", 0, "Minimum multiplicity (MultNtr)"}; Configurable multMax{"multMax", 99999, "Maximum multiplicity (MultNtr)"}; @@ -102,7 +102,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { struct : ConfigurableGroup { Configurable doMixEvent{"doMixEvent", true, "Enable mixed events"}; Configurable mixingBinPolicy{ - "mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; + "mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; Configurable mixingDepth{"mixingDepth", 5, "Number of neighbours for O2 event mixing"}; } mixSetting; @@ -110,59 +110,61 @@ struct HfTaskCharmHadronsCharmFemtoDream { {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; ConfigurableAxis mixingBinMultPercentile{ - "mixingBinMultPercentile", - {VARIABLE_WIDTH, 0.f, 100.f}, - "Mixing bins - multiplicity percentile"}; + "mixingBinMultPercentile", + {VARIABLE_WIDTH, 0.f, 100.f}, + "Mixing bins - multiplicity percentile"}; ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; ColumnBinningPolicy - colBinningMult{{mixingBinVztx, mixingBinMult}, true}; + colBinningMult{{mixingBinVztx, mixingBinMult}, true}; ColumnBinningPolicy - colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; + colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; ColumnBinningPolicy - colBinningMultMultPercentile{ - {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMultMultPercentile{ + {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, + true}; aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; using FilteredCollisions = - soa::Filtered>; + soa::Filtered>; using FilteredCharmCand2Prongs = soa::Filtered; using FilteredCharmCandDstars = soa::Filtered; Filter eventMultiplicity = - aod::femtodreamcollision::multNtr >= eventSel.multMin && - aod::femtodreamcollision::multNtr <= eventSel.multMax; + aod::femtodreamcollision::multNtr >= eventSel.multMin && + aod::femtodreamcollision::multNtr <= eventSel.multMax; Filter eventMultiplicityPercentile = ifnode( - eventSel.useCentrality, - aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && - aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, - Node{LiteralNode{true}}); + eventSel.useCentrality, + aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && + aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, + Node{LiteralNode{true}}); Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmHadCandSel; Partition partitionCharmHadron2Prong = ifnode( - useMl, - aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, - Node{LiteralNode{true}}); + useMl, + aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, + Node{LiteralNode{true}}); Partition partitionCharmHadronDstar = - ifnode(useMl, - aod::fdhf::bdtBkg <= maxBkgDstar && - aod::fdhf::bdtPrompt >= minPromptDstar, - Node{LiteralNode{true}}); + ifnode(useMl, + aod::fdhf::bdtBkg <= maxBkgDstar && + aod::fdhf::bdtPrompt >= minPromptDstar, + Node{LiteralNode{true}}); // Full-table process inputs do not automatically register grouping caches. // Register both keys explicitly for the sliceByCached calls below. Preslice perCollisionD0 = - aod::femtodreamparticle::fdCollisionId; + aod::femtodreamparticle::fdCollisionId; Preslice perCollisionDstar = - aod::femtodreamparticle::fdCollisionId; + aod::femtodreamparticle::fdCollisionId; SliceCache cache; HistogramRegistry registry{"registry"}; Produces rowFemtoResultCharm2Prong; Produces rowFemtoResultCharmDstar; Produces rowFemtoResultColl; - void init(InitContext const &) { + void init(InitContext const&) + { if (doprocessD0D0 == doprocessD0Dstar) { LOGP(fatal, "Enable exactly one charm-charm analysis process"); } @@ -178,7 +180,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; colBinningMultMultPercentile = { - {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; const AxisSpec massD0{300, massMinD0.value, massMaxD0.value, "M(Kpi) (GeV/c2)"}; @@ -215,7 +217,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - static bool sharesDaughter(D0Row const &a, OtherRow const &b) { + static bool sharesDaughter(D0Row const& a, OtherRow const& b) + { for (auto id : std::array{a.prong0Id(), a.prong1Id()}) { if (id < 0) { continue; @@ -234,11 +237,12 @@ struct HfTaskCharmHadronsCharmFemtoDream { // D0 charge denotes flavour, not electric charge. template - static PairChannel pairChannel(D0Row const &d0, OtherRow const &other) { + static PairChannel pairChannel(D0Row const& d0, OtherRow const& other) + { if constexpr (!IsDstar) { return d0.charge() != other.charge() - ? D0D0barUnlikeSign - : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); + ? D0D0barUnlikeSign + : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); } return d0.charge() > 0 ? (other.charge() > 0 ? D0DstarPlusLikeSign : D0DstarMinusUnlikeSign) @@ -249,7 +253,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Canonical ordering keeps mass/pt axes independent of event ordering for // D0D0. template - static bool reverseD0Order(FirstRow const &first, SecondRow const &second) { + static bool reverseD0Order(FirstRow const& first, SecondRow const& second) + { if (first.charge() != second.charge()) { return first.charge() < second.charge(); // D0 first for D0-D0bar } @@ -257,21 +262,24 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void validateMlScores(Candidates const &candidates) { + void validateMlScores(Candidates const& candidates) + { if (!useMl) { return; } - for (auto const &row : candidates) { + for (auto const& row : candidates) { if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { - LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML " - "producer process"); + LOGP(fatal, + "ML cuts requested on missing/invalid scores: use an ML " + "producer process"); } } } template - bool select(Row const &row, float &mass) { + bool select(Row const& row, float& mass) + { if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || std::abs(row.eta()) >= etaMax || @@ -280,8 +288,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { return false; } const std::array masses = - row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} - : std::array{MassKPlus, MassPiPlus}; + row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} + : std::array{MassKPlus, MassPiPlus}; if constexpr (IsDstar) { const float daughterMass = row.mDaughD0(masses); if (!std::isfinite(daughterMass) || daughterMass < daughterMassMin || @@ -310,8 +318,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void fillPair(FirstRow const &first, SecondRow const &second, float firstMass, - float secondMass, float mult) { + void fillPair(FirstRow const& first, SecondRow const& second, float firstMass, + float secondMass, float mult) + { if constexpr (!Mixed) { if (sharesDaughter(first, second)) { registry.fill(HIST("QA/sharedDaughters"), IsDstar ? 1 : 0); @@ -326,9 +335,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Read kinematics directly from the candidate rows. Only D0D0 can reverse; // its two nominal parent masses are equal. Never use Dstar delta mass here. const float kstar = - reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) - : FemtoDreamMath::getkstar(first, MassD0, second, - IsDstar ? MassDStar : MassD0); + reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) + : FemtoDreamMath::getkstar(first, MassD0, second, + IsDstar ? MassDStar : MassD0); if (!std::isfinite(kstar)) { return; } @@ -349,33 +358,35 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - int mixingBin(Policy const &policy, Collision col) { + int mixingBin(Policy const& policy, Collision col) + { return policy.getBin(policy.getBinningValues(col)); } template - void doMixedEvent(FilteredCollisions const &cols, D0Table const &d0Rows, - OtherTable const &otherRows, Policy const &policy) { + void doMixedEvent(FilteredCollisions const& cols, D0Table const& d0Rows, + OtherTable const& otherRows, Policy const& policy) + { Partition partitionMaskedCol1 = - (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. Partition partitionMaskedCol2 = - (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; Partition partitionMaskedColDstar = - (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; + (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; partitionMaskedCol1.bindTable(cols); if constexpr (IsDstar) { partitionMaskedColDstar.bindTable(cols); } else { partitionMaskedCol2.bindTable(cols); } - auto const &secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered + auto const& secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered : *partitionMaskedCol2.mFiltered; - for (auto const &[collision1, collision2] : + for (auto const& [collision1, collision2] : combinations(soa::CombinationsBlockFullIndexPolicy( - policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, - secondCollisions))) { + policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, + secondCollisions))) { if (collision1.globalIndex() == collision2.globalIndex()) { continue; } @@ -400,11 +411,11 @@ struct HfTaskCharmHadronsCharmFemtoDream { auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); auto second = - otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, - collision2.globalIndex(), cache); + otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, + collision2.globalIndex(), cache); // Species roles remain fixed; the full policy supplies the reverse event // orientation when eligible. Do not add a second manual reverse loop. - for (auto const &[row1, row2] : + for (auto const& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(first, second))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { @@ -419,8 +430,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Reuse the same output tables and field conventions as the track/V0 tasks. template - void fillTables(CollType const &col, D0Slice const &d0s, - DstarSlice const &dstars) { + void fillTables(CollType const& col, D0Slice const& d0s, + DstarSlice const& dstars) + { int64_t timeStamp = -1; bool hasCandidate = false; auto recordTimeStamp = [&](int64_t value) { @@ -430,7 +442,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { timeStamp = value; hasCandidate = true; }; - for (auto const &part : d0s) { + for (auto const& part : d0s) { float mass{}; if (!select(part, mass)) { continue; @@ -442,21 +454,21 @@ struct HfTaskCharmHadronsCharmFemtoDream { part.bdtPrompt(), part.bdtFD(), 0, 0); } if constexpr (WithDstar) { - for (auto const &part : dstars) { + for (auto const& part : dstars) { float mass{}; if (!select(part, mass)) { continue; } recordTimeStamp(part.timeStamp()); const std::array daughterMasses = - part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} - : std::array{MassKPlus, MassPiPlus}; + part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} + : std::array{MassKPlus, MassPiPlus}; // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. rowFemtoResultCharmDstar( - col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), - part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), - part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD(), 0, 0); + col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), + part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), + part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), + part.bdtFD(), 0, 0); } } // Retain D0-only/Dstar-only events even when no clean SE pair exists. @@ -469,16 +481,17 @@ struct HfTaskCharmHadronsCharmFemtoDream { template - void runWithPolicy(FilteredCollisions const &cols, D0Table const &d0Rows, - DstarTable const &dstarRows, Policy const &policy) { - for (auto const &col : cols) { + void runWithPolicy(FilteredCollisions const& cols, D0Table const& d0Rows, + DstarTable const& dstarRows, Policy const& policy) + { + for (auto const& col : cols) { auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); // Export before online mixing-bin cuts, so offline mixing can change // bins. if constexpr (WithDstar) { auto dstars = dstarRows.sliceByCached( - aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); fillTables(col, d0s, dstars); } else { fillTables(col, d0s, d0s); @@ -490,7 +503,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { } registry.fill(HIST("QA/events"), 0); registry.fill(HIST("MixingQA/hSECollisionBins"), bin); - for (auto const &[row1, row2] : + for (auto const& [row1, row2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { @@ -499,8 +512,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { } if constexpr (WithDstar) { auto dstars = dstarRows.sliceByCached( - aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); - for (auto const &[row1, row2] : + aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + for (auto const& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { @@ -518,9 +531,10 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void run(FilteredCollisions const &cols, - FilteredCharmCand2Prongs const &d0Rows, - DstarTable const &dstarRows) { + void run(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0Rows, + DstarTable const& dstarRows) + { validateMlScores(d0Rows); partitionCharmHadron2Prong.bindTable(d0Rows); if constexpr (WithDstar) { @@ -528,42 +542,45 @@ struct HfTaskCharmHadronsCharmFemtoDream { partitionCharmHadronDstar.bindTable(dstarRows); } switch (mixSetting.mixingBinPolicy) { - case aod::femtodreamcollision::kMult: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, colBinningMult); - break; - case aod::femtodreamcollision::kMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, - colBinningMultPercentile); - break; - case aod::femtodreamcollision::kMultMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, - colBinningMultMultPercentile); - break; - default: - LOGP(fatal, "Invalid mixing binning policy"); + case aod::femtodreamcollision::kMult: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, colBinningMult); + break; + case aod::femtodreamcollision::kMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, + colBinningMultPercentile); + break; + case aod::femtodreamcollision::kMultMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, + colBinningMultMultPercentile); + break; + default: + LOGP(fatal, "Invalid mixing binning policy"); } } - void processD0D0(FilteredCollisions const &cols, - FilteredCharmCand2Prongs const &d0s) { + void processD0D0(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s) + { run(cols, d0s, d0s); } PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, "D0D0 data, SE and ME", true); - void processD0Dstar(FilteredCollisions const &cols, - FilteredCharmCand2Prongs const &d0s, - FilteredCharmCandDstars const &dstars) { + void processD0Dstar(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s, + FilteredCharmCandDstars const& dstars) + { run(cols, d0s, dstars); } PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, "D0D0 and D0Dstar data, SE and ME", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const &cfgc) { +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } From 634bfabf46b4bade20054ac933f20238d48ca5f9 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Mon, 21 Sep 2026 21:32:53 +0200 Subject: [PATCH 14/23] PWGHF: Clean charm femto linter warnings --- .../producerCharmHadronsCharmFemtoDream.cxx | 227 +++++++-------- .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 264 ++++++++---------- 2 files changed, 210 insertions(+), 281 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 848a74fcfca..5142345fc97 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -26,7 +26,6 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Tools/ML/MlResponse.h" #include #include @@ -44,6 +43,7 @@ #include #include #include +#include #include #include @@ -62,81 +62,66 @@ struct HfProducerCharmHadronsCharmFemtoDream { FillMlFromSelector = 1, FillMlFromNewBDT = 2 }; - Configurable selectionFlagD0{"selectionFlagD0", 1, - "Minimum D0 selector decision"}; - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", - "CCDB URL"}; - Configurable ccdbPathGrpMag{ - "ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; - + static constexpr std::size_t NMlClasses = 3; + static constexpr std::size_t NMlPtEdgesMin = 2; + static constexpr int ND0Hypotheses = 2; // Each species needs its own model, feature order and pT-dependent cuts. - struct MlConfig : ConfigurableGroup { + struct HfMlConfig : ConfigurableGroup { std::string prefix; - static inline const std::array DefaultCuts{1., 0., 0.}; - Configurable applyMlMode{ - "applyMlMode", FillMlFromSelector, - "0: no ML, 1: selector scores, 2: new BDT after selector"}; - Configurable> binsPtMl{ - "binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; - Configurable> cutsMl{ - "cutsMl", - {DefaultCuts.data(), 1, 3}, - "New BDT cuts per pT bin: background, prompt, nonprompt"}; - Configurable> cutDirMl{ - "cutDirMl", std::vector{0, 1, 1}, - "Reject scores above (0), below (1), or do not cut (2)"}; - Configurable nClassesMl{ - "nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; - Configurable> namesInputFeatures{ - "namesInputFeatures", std::vector{}, - "Ordered input feature names for new BDT"}; - Configurable> onnxFileNames{ - "onnxFileNames", std::vector{}, - "Model files, one per pT bin"}; - Configurable> modelPathsCCDB{ - "modelPathsCCDB", std::vector{}, - "CCDB model paths, one per pT bin"}; - Configurable timestampCCDB{"timestampCCDB", -1, - "Timestamp used to retrieve models"}; - Configurable loadModelsFromCCDB{ - "loadModelsFromCCDB", false, - "Load new BDT from CCDB instead of local files"}; - } mlD0{.prefix = "mlD0"}, mlDstar{.prefix = "mlDstar"}; + static inline const std::array defaultCuts{1., 0., 0.}; + Configurable applyMlMode{"applyMlMode", FillMlFromSelector, "0: no ML, 1: selector scores, 2: new BDT after selector"}; + Configurable> binsPtMl{"binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; + Configurable> cutsMl{"cutsMl", {defaultCuts.data(), 1, 3}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; + Configurable> cutDirMl{"cutDirMl", std::vector{0, 1, 1}, "Reject scores above (0), below (1), or do not cut (2)"}; + Configurable nClassesMl{"nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{}, "Ordered input feature names for new BDT"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{}, "Model files, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{}, "CCDB model paths, one per pT bin"}; + Configurable timestampCCDB{"timestampCCDB", -1, "Timestamp used to retrieve models"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Load new BDT from CCDB instead of local files"}; + }; + Produces collisions; Produces rowMasks; Produces d0Rows; Produces dstarRows; + Configurable selectionFlagD0{"selectionFlagD0", 1, "Minimum D0 selector decision"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; + HfMlConfig mlD0{.prefix = "mlD0"}, mlDstar{.prefix = "mlDstar"}; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; o2::hf_evsel::HfEventSelection hfEvSel; o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; - OutputObj zorroSummary{"zorroSummary"}; - HistogramRegistry registry{"registry"}; - bool hasD0 = false; - bool hasDstar = false; - int runNumber = -1; using Collisions = soa::Join; using CollisionsWithFT0C = soa::Join; using D0s = soa::Join; using Dstars = - soa::Join; + soa::Join; using D0sMl = soa::Join; using DstarsMl = soa::Join; Filter filterSelectCandidateD0 = - (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || - aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); + (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || + aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); Filter filterSelectCandidateDstar = - aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + + HistogramRegistry registry{"registry"}; + OutputObj zorroSummary{"zorroSummary"}; + + bool hasD0 = false; + bool hasDstar = false; + int runNumber = -1; template - void initMl(MlConfig const& cfg, Response& response, bool withMl) - { + void initMl(HfMlConfig const&cfg, Response &response, bool withMl) { if (cfg.applyMlMode.value < NoMl || cfg.applyMlMode.value > FillMlFromNewBDT) { LOGP(fatal, "{}: invalid applyMlMode", cfg.prefix); @@ -147,8 +132,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { if (!withMl) { LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); } - auto const& edges = cfg.binsPtMl.value; - if (edges.size() < 2 || + auto const&edges = cfg.binsPtMl.value; + if (edges.size() < NMlPtEdgesMin || !std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }) || std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { @@ -158,8 +143,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { cfg.prefix); } const auto nBins = edges.size() - 1; - if (cfg.nClassesMl.value != 3 || cfg.cutDirMl.value.size() != 3 || - cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != 3 || + if (cfg.nClassesMl.value != NMlClasses || cfg.cutDirMl.value.size() != NMlClasses || + cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != NMlClasses || cfg.onnxFileNames.value.size() != nBins || cfg.namesInputFeatures.value.empty() || (cfg.loadModelsFromCCDB.value && @@ -169,14 +154,14 @@ struct HfProducerCharmHadronsCharmFemtoDream { "features", cfg.prefix); } - for (auto direction : cfg.cutDirMl.value) { + for (const auto& direction : cfg.cutDirMl.value) { if (direction < o2::cuts_ml::CutGreater || direction > o2::cuts_ml::CutNot) { LOGP(fatal, "{}: invalid cutDirMl", cfg.prefix); } } for (unsigned int bin = 0; bin < nBins; ++bin) { - for (unsigned int score = 0; score < 3; ++score) { + for (unsigned int score = 0; score < NMlClasses; ++score) { if (!std::isfinite(cfg.cutsMl.value.get(bin, score))) { LOGP(fatal, "{}: cutsMl must be finite", cfg.prefix); } @@ -196,17 +181,16 @@ struct HfProducerCharmHadronsCharmFemtoDream { response.init(); } - void init(InitContext const&) - { + void init(InitContext const&) { const std::array processSwitches{ - static_cast(doprocessD0D0), - static_cast(doprocessD0Dstar), - static_cast(doprocessD0D0Ml), - static_cast(doprocessD0DstarMl), - static_cast(doprocessD0D0WithFT0C), - static_cast(doprocessD0DstarWithFT0C), - static_cast(doprocessD0D0MlWithFT0C), - static_cast(doprocessD0DstarMlWithFT0C)}; + static_cast(doprocessD0D0), + static_cast(doprocessD0Dstar), + static_cast(doprocessD0D0Ml), + static_cast(doprocessD0DstarMl), + static_cast(doprocessD0D0WithFT0C), + static_cast(doprocessD0DstarWithFT0C), + static_cast(doprocessD0D0MlWithFT0C), + static_cast(doprocessD0DstarMlWithFT0C)}; if (std::count(processSwitches.begin(), processSwitches.end(), true) != 1) { LOGP(fatal, "Enable exactly one charm-charm producer process"); } @@ -232,12 +216,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { template - bool acceptCollision(Collision const& col) - { + bool acceptCollision(Collision const&col) { registry.fill(HIST("events"), 0); float cent = -1.f; // No centrality for pp MB. const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask< - true, CentEstimator, aod::BCsWithTimestamps>(col, cent, ccdb, registry); + true, CentEstimator, aod::BCsWithTimestamps>(col, cent, ccdb, registry); hfEvSel.fillHistograms(col, rejectionMask, cent); if (rejectionMask != 0) { return false; @@ -248,7 +231,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { initCCDB(bc, runNumber, ccdb, ccdbPathGrpMag.value, nullptr, false); // Propagator field is in kG; FemtoDream uses Tesla. const float fieldTesla = - 0.1f * o2::base::Propagator::Instance()->getNominalBz(); + 0.1f * o2::base::Propagator::Instance()->getNominalBz(); // MultV0M is the common reduced percentile column: FT0C or -1 (no // estimator). collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, fieldTesla); @@ -258,14 +241,13 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - std::array readScores(Scores const& scores) - { - if (scores.size() != 3) { + std::array readScores(Scores const&scores) { + if (scores.size() != NMlClasses) { LOGP(fatal, "Expected three selector ML scores: background, prompt, nonprompt"); return {-1.f, -1.f, -1.f}; } - for (auto score : scores) { + for (const auto& score : scores) { if (!std::isfinite(score)) { LOGP(fatal, "Non-finite selector ML score"); } @@ -274,16 +256,15 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillD0(Collision const& col, Candidates const& candidates) - { + void fillD0(Collision const&col, Candidates const&candidates) { const auto timestamp = - col.template bc_as().timestamp(); - for (auto const& cand : candidates) { + col.template bc_as().timestamp(); + for (const auto& cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); // The OR filter accepts the row if either hypothesis passes. Write only // the passing hypotheses, each with its own flavour and ML scores. - for (int hypothesis = 0; hypothesis < 2; ++hypothesis) { + for (int hypothesis = 0; hypothesis < ND0Hypotheses; ++hypothesis) { if ((hypothesis == 0 ? cand.isSelD0() : cand.isSelD0bar()) < selectionFlagD0) { continue; @@ -304,7 +285,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 : -o2::constants::physics::kD0; auto features = - hfMlResponseD0.getInputFeatures(cand, pdgCode); + hfMlResponseD0.getInputFeatures(cand, pdgCode); std::vector output; if (!hfMlResponseD0.isSelectedMl(features, cand.pt(), output)) { continue; // Reject this hypothesis only. @@ -323,11 +304,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillDstar(Collision const& col, Candidates const& candidates) - { + void fillDstar(Collision const&col, Candidates const&candidates) { const auto timestamp = - col.template bc_as().timestamp(); - for (auto const& cand : candidates) { + col.template bc_as().timestamp(); + for (const auto& cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); auto soft = cand.template prongPi_as(); @@ -360,118 +340,99 @@ struct HfProducerCharmHadronsCharmFemtoDream { } } - void processD0D0(Collisions::iterator const& col, + void processD0D0(Collisions::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + soa::Filtered const&d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, - "D0 only, data", true); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); - void processD0Dstar(Collisions::iterator const& col, + void processD0Dstar(Collisions::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, - soa::Filtered const& dstars) - { + soa::Filtered const&d0s, + soa::Filtered const&dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, - "D0 and Dstar, data", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); - void processD0D0Ml(Collisions::iterator const& col, + void processD0D0Ml(Collisions::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + soa::Filtered const&d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, - "D0 with selector ML scores", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); - void processD0DstarMl(Collisions::iterator const& col, + void processD0DstarMl(Collisions::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, - soa::Filtered const& dstars) - { + soa::Filtered const&d0s, + soa::Filtered const&dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, - "D0 and Dstar with selector ML scores", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); - void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, + void processD0D0WithFT0C(CollisionsWithFT0C::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + soa::Filtered const&d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, - "D0 only, data with FT0C centrality", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, "D0 only, data with FT0C centrality", false); - void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const& col, + void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, - soa::Filtered const& dstars) - { + soa::Filtered const&d0s, + soa::Filtered const&dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, - "D0 and Dstar, data with FT0C centrality", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, "D0 and Dstar, data with FT0C centrality", false); - void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const& col, + void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s) - { + soa::Filtered const&d0s) { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, - "D0 with selector ML scores and FT0C centrality", false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, "D0 with selector ML scores and FT0C centrality", false); - void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const& col, + void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const&col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const& d0s, - soa::Filtered const& dstars) - { + soa::Filtered const&d0s, + soa::Filtered const&dstars) { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); } } - PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, - processD0DstarMlWithFT0C, - "D0 and Dstar with selector ML scores and FT0C centrality", - false); + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMlWithFT0C, "D0 and Dstar with selector ML scores and FT0C centrality", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ +WorkflowSpec defineDataProcessing(ConfigContext const&cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index 6f01cc5678c..334ce094e73 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -57,6 +57,11 @@ struct HfTaskCharmHadronsCharmFemtoDream { D0barDstarPlusUnlikeSign = 6, NPairChannels = 7 }; + static constexpr int MixingBinPolicyMax = 2; + + Produces rowFemtoResultCharm2Prong; + Produces rowFemtoResultCharmDstar; + Produces rowFemtoResultColl; Configurable ptMinD0{"ptMinD0", 0.f, "Minimum D0 pT"}; Configurable ptMaxD0{"ptMaxD0", 36.f, "Maximum D0 pT"}; @@ -66,7 +71,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { Configurable massMinD0{"massMinD0", 1.7f, "Minimum D0 mass (keep sidebands)"}; Configurable massMaxD0{"massMaxD0", 2.0f, "Maximum D0 mass"}; - Configurable deltaMassMin{"deltaMassMin", 0.139f, + Configurable deltaMassMin{"deltaMassMin", MassPiPlus, "Minimum Dstar-D0 mass difference"}; Configurable deltaMassMax{"deltaMassMax", 0.17f, "Maximum Dstar-D0 mass difference"}; @@ -83,13 +88,10 @@ struct HfTaskCharmHadronsCharmFemtoDream { "Maximum Dstar background score"}; Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; - Configurable charmHadCandSel{ - "charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; + Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { std::string prefix = "eventSel"; - Configurable useCentrality{ - "useCentrality", false, - "Apply percentile selection (requires a centrality-enabled producer)"}; + Configurable useCentrality{"useCentrality", false, "Apply percentile selection (requires a centrality-enabled producer)"}; Configurable multMin{"multMin", 0, "Minimum multiplicity (MultNtr)"}; Configurable multMax{"multMax", 99999, "Maximum multiplicity (MultNtr)"}; @@ -101,75 +103,58 @@ struct HfTaskCharmHadronsCharmFemtoDream { struct : ConfigurableGroup { Configurable doMixEvent{"doMixEvent", true, "Enable mixed events"}; - Configurable mixingBinPolicy{ - "mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; + Configurable mixingBinPolicy{"mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; Configurable mixingDepth{"mixingDepth", 5, "Number of neighbours for O2 event mixing"}; } mixSetting; - ConfigurableAxis mixingBinMult{"mixingBinMult", - {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, - "Mixing bins - multiplicity"}; - ConfigurableAxis mixingBinMultPercentile{ - "mixingBinMultPercentile", - {VARIABLE_WIDTH, 0.f, 100.f}, - "Mixing bins - multiplicity percentile"}; - ConfigurableAxis mixingBinVztx{"mixingBinVztx", - {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, - "Mixing bins - z-vertex"}; - ColumnBinningPolicy - colBinningMult{{mixingBinVztx, mixingBinMult}, true}; - ColumnBinningPolicy - colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; - ColumnBinningPolicy - colBinningMultMultPercentile{ - {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, - true}; - aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; + SliceCache cache; using FilteredCollisions = - soa::Filtered>; + soa::Filtered>; using FilteredCharmCand2Prongs = soa::Filtered; using FilteredCharmCandDstars = soa::Filtered; Filter eventMultiplicity = - aod::femtodreamcollision::multNtr >= eventSel.multMin && - aod::femtodreamcollision::multNtr <= eventSel.multMax; + aod::femtodreamcollision::multNtr >= eventSel.multMin && + aod::femtodreamcollision::multNtr <= eventSel.multMax; Filter eventMultiplicityPercentile = ifnode( - eventSel.useCentrality, - aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && - aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, - Node{LiteralNode{true}}); + eventSel.useCentrality, + aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && + aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, + Node{LiteralNode{true}}); Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmHadCandSel; - Partition partitionCharmHadron2Prong = ifnode( - useMl, - aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, - Node{LiteralNode{true}}); - Partition partitionCharmHadronDstar = - ifnode(useMl, - aod::fdhf::bdtBkg <= maxBkgDstar && - aod::fdhf::bdtPrompt >= minPromptDstar, - Node{LiteralNode{true}}); - // Full-table process inputs do not automatically register grouping caches. // Register both keys explicitly for the sliceByCached calls below. Preslice perCollisionD0 = - aod::femtodreamparticle::fdCollisionId; + aod::femtodreamparticle::fdCollisionId; Preslice perCollisionDstar = - aod::femtodreamparticle::fdCollisionId; - SliceCache cache; + aod::femtodreamparticle::fdCollisionId; + + Partition partitionCharmHadron2Prong = ifnode( + useMl, + aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, + Node{LiteralNode{true}}); + Partition partitionCharmHadronDstar = + ifnode(useMl, + aod::fdhf::bdtBkg <= maxBkgDstar && + aod::fdhf::bdtPrompt >= minPromptDstar, + Node{LiteralNode{true}}); + + ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; + ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; + ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; HistogramRegistry registry{"registry"}; - Produces rowFemtoResultCharm2Prong; - Produces rowFemtoResultCharmDstar; - Produces rowFemtoResultColl; - void init(InitContext const&) - { + void init(InitContext const&) { if (doprocessD0D0 == doprocessD0Dstar) { LOGP(fatal, "Enable exactly one charm-charm analysis process"); } if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || - mixSetting.mixingBinPolicy > 2 || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || + mixSetting.mixingBinPolicy > MixingBinPolicyMax || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || ptMinDstar < 0 || ptMinDstar >= ptMaxDstar || etaMax <= 0 || massMinD0 >= massMaxD0 || deltaMassMin >= deltaMassMax || daughterMassMin >= daughterMassMax || charmHadCandSel < 1 || @@ -180,7 +165,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; colBinningMultMultPercentile = { - {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; const AxisSpec massD0{300, massMinD0.value, massMaxD0.value, "M(Kpi) (GeV/c2)"}; @@ -217,9 +202,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - static bool sharesDaughter(D0Row const& a, OtherRow const& b) - { - for (auto id : std::array{a.prong0Id(), a.prong1Id()}) { + static bool sharesDaughter(D0Row const&a, OtherRow const&b) { + for (const auto& id : std::array{a.prong0Id(), a.prong1Id()}) { if (id < 0) { continue; } @@ -237,12 +221,11 @@ struct HfTaskCharmHadronsCharmFemtoDream { // D0 charge denotes flavour, not electric charge. template - static PairChannel pairChannel(D0Row const& d0, OtherRow const& other) - { + static PairChannel pairChannel(D0Row const&d0, OtherRow const&other) { if constexpr (!IsDstar) { return d0.charge() != other.charge() - ? D0D0barUnlikeSign - : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); + ? D0D0barUnlikeSign + : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); } return d0.charge() > 0 ? (other.charge() > 0 ? D0DstarPlusLikeSign : D0DstarMinusUnlikeSign) @@ -253,8 +236,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Canonical ordering keeps mass/pt axes independent of event ordering for // D0D0. template - static bool reverseD0Order(FirstRow const& first, SecondRow const& second) - { + static bool reverseD0Order(FirstRow const&first, SecondRow const&second) { if (first.charge() != second.charge()) { return first.charge() < second.charge(); // D0 first for D0-D0bar } @@ -262,24 +244,21 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void validateMlScores(Candidates const& candidates) - { + void validateMlScores(Candidates const&candidates) { if (!useMl) { return; } - for (auto const& row : candidates) { + for (const auto& row : candidates) { if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { - LOGP(fatal, - "ML cuts requested on missing/invalid scores: use an ML " - "producer process"); + LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML " + "producer process"); } } } template - bool select(Row const& row, float& mass) - { + bool select(Row const&row, float &mass) { if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || std::abs(row.eta()) >= etaMax || @@ -288,8 +267,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { return false; } const std::array masses = - row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} - : std::array{MassKPlus, MassPiPlus}; + row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} + : std::array{MassKPlus, MassPiPlus}; if constexpr (IsDstar) { const float daughterMass = row.mDaughD0(masses); if (!std::isfinite(daughterMass) || daughterMass < daughterMassMin || @@ -318,9 +297,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void fillPair(FirstRow const& first, SecondRow const& second, float firstMass, - float secondMass, float mult) - { + void fillPair(FirstRow const&first, SecondRow const&second, float firstMass, + float secondMass, float mult) { if constexpr (!Mixed) { if (sharesDaughter(first, second)) { registry.fill(HIST("QA/sharedDaughters"), IsDstar ? 1 : 0); @@ -335,9 +313,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Read kinematics directly from the candidate rows. Only D0D0 can reverse; // its two nominal parent masses are equal. Never use Dstar delta mass here. const float kstar = - reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) - : FemtoDreamMath::getkstar(first, MassD0, second, - IsDstar ? MassDStar : MassD0); + reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) + : FemtoDreamMath::getkstar(first, MassD0, second, + IsDstar ? MassDStar : MassD0); if (!std::isfinite(kstar)) { return; } @@ -358,35 +336,33 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - int mixingBin(Policy const& policy, Collision col) - { + int mixingBin(Policy const&policy, Collision col) { return policy.getBin(policy.getBinningValues(col)); } template - void doMixedEvent(FilteredCollisions const& cols, D0Table const& d0Rows, - OtherTable const& otherRows, Policy const& policy) - { + void doMixedEvent(FilteredCollisions const&cols, D0Table const&d0Rows, + OtherTable const&otherRows, Policy const&policy) { Partition partitionMaskedCol1 = - (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. Partition partitionMaskedCol2 = - (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; Partition partitionMaskedColDstar = - (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; + (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; partitionMaskedCol1.bindTable(cols); if constexpr (IsDstar) { partitionMaskedColDstar.bindTable(cols); } else { partitionMaskedCol2.bindTable(cols); } - auto const& secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered + auto const&secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered : *partitionMaskedCol2.mFiltered; - for (auto const& [collision1, collision2] : + for (const auto& [collision1, collision2] : combinations(soa::CombinationsBlockFullIndexPolicy( - policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, - secondCollisions))) { + policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, + secondCollisions))) { if (collision1.globalIndex() == collision2.globalIndex()) { continue; } @@ -411,11 +387,11 @@ struct HfTaskCharmHadronsCharmFemtoDream { auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); auto second = - otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, - collision2.globalIndex(), cache); + otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, + collision2.globalIndex(), cache); // Species roles remain fixed; the full policy supplies the reverse event // orientation when eligible. Do not add a second manual reverse loop. - for (auto const& [row1, row2] : + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(first, second))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { @@ -430,9 +406,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Reuse the same output tables and field conventions as the track/V0 tasks. template - void fillTables(CollType const& col, D0Slice const& d0s, - DstarSlice const& dstars) - { + void fillTables(CollType const&col, D0Slice const&d0s, + DstarSlice const&dstars) { int64_t timeStamp = -1; bool hasCandidate = false; auto recordTimeStamp = [&](int64_t value) { @@ -442,7 +417,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { timeStamp = value; hasCandidate = true; }; - for (auto const& part : d0s) { + for (const auto& part : d0s) { float mass{}; if (!select(part, mass)) { continue; @@ -454,21 +429,21 @@ struct HfTaskCharmHadronsCharmFemtoDream { part.bdtPrompt(), part.bdtFD(), 0, 0); } if constexpr (WithDstar) { - for (auto const& part : dstars) { + for (const auto& part : dstars) { float mass{}; if (!select(part, mass)) { continue; } recordTimeStamp(part.timeStamp()); const std::array daughterMasses = - part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} - : std::array{MassKPlus, MassPiPlus}; + part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} + : std::array{MassKPlus, MassPiPlus}; // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. rowFemtoResultCharmDstar( - col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), - part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), - part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD(), 0, 0); + col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), + part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), + part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), + part.bdtFD(), 0, 0); } } // Retain D0-only/Dstar-only events even when no clean SE pair exists. @@ -481,17 +456,16 @@ struct HfTaskCharmHadronsCharmFemtoDream { template - void runWithPolicy(FilteredCollisions const& cols, D0Table const& d0Rows, - DstarTable const& dstarRows, Policy const& policy) - { - for (auto const& col : cols) { + void runWithPolicy(FilteredCollisions const&cols, D0Table const&d0Rows, + DstarTable const&dstarRows, Policy const&policy) { + for (const auto& col : cols) { auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); // Export before online mixing-bin cuts, so offline mixing can change // bins. if constexpr (WithDstar) { auto dstars = dstarRows.sliceByCached( - aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); fillTables(col, d0s, dstars); } else { fillTables(col, d0s, d0s); @@ -503,7 +477,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { } registry.fill(HIST("QA/events"), 0); registry.fill(HIST("MixingQA/hSECollisionBins"), bin); - for (auto const& [row1, row2] : + for (const auto& [row1, row2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { @@ -512,8 +486,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { } if constexpr (WithDstar) { auto dstars = dstarRows.sliceByCached( - aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); - for (auto const& [row1, row2] : + aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { @@ -531,10 +505,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void run(FilteredCollisions const& cols, - FilteredCharmCand2Prongs const& d0Rows, - DstarTable const& dstarRows) - { + void run(FilteredCollisions const&cols, + FilteredCharmCand2Prongs const&d0Rows, + DstarTable const&dstarRows) { validateMlScores(d0Rows); partitionCharmHadron2Prong.bindTable(d0Rows); if constexpr (WithDstar) { @@ -542,45 +515,40 @@ struct HfTaskCharmHadronsCharmFemtoDream { partitionCharmHadronDstar.bindTable(dstarRows); } switch (mixSetting.mixingBinPolicy) { - case aod::femtodreamcollision::kMult: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, colBinningMult); - break; - case aod::femtodreamcollision::kMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, - colBinningMultPercentile); - break; - case aod::femtodreamcollision::kMultMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, - colBinningMultMultPercentile); - break; - default: - LOGP(fatal, "Invalid mixing binning policy"); + case aod::femtodreamcollision::kMult: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, colBinningMult); + break; + case aod::femtodreamcollision::kMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, + colBinningMultPercentile); + break; + case aod::femtodreamcollision::kMultMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, + partitionCharmHadronDstar, + colBinningMultMultPercentile); + break; + default: + LOGP(fatal, "Invalid mixing binning policy"); } } - void processD0D0(FilteredCollisions const& cols, - FilteredCharmCand2Prongs const& d0s) - { + void processD0D0(FilteredCollisions const&cols, + FilteredCharmCand2Prongs const&d0s) { run(cols, d0s, d0s); } - PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, - "D0D0 data, SE and ME", true); + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, "D0D0 data, SE and ME", true); - void processD0Dstar(FilteredCollisions const& cols, - FilteredCharmCand2Prongs const& d0s, - FilteredCharmCandDstars const& dstars) - { + void processD0Dstar(FilteredCollisions const&cols, + FilteredCharmCand2Prongs const&d0s, + FilteredCharmCandDstars const&dstars) { run(cols, d0s, dstars); } - PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, - "D0D0 and D0Dstar data, SE and ME", false); + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, "D0D0 and D0Dstar data, SE and ME", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ +WorkflowSpec defineDataProcessing(ConfigContext const&cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } From 6a4cb4a35baeb932f7ff92722b3338ea82125f6d Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Mon, 21 Sep 2026 21:38:17 +0200 Subject: [PATCH 15/23] PWGHF: Simplify charm femto histogram setup --- .../producerCharmHadronsCharmFemtoDream.cxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 5142345fc97..3575b194ccf 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -208,10 +208,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { initMl(mlDstar, hfMlResponseDstar, withMl); } hfEvSel.init(registry, &zorroSummary); - registry.add("events", ";stage (0=all,1=accepted);events", kTH1F, - {{2, -0.5, 1.5}}); - registry.add("candidates", ";species (0=D0,1=D0bar,2=Dstar+,3=Dstar-);rows", - kTH1F, {{4, -0.5, 3.5}}); + const AxisSpec eventStage{2, -0.5, 1.5, "stage (0=all,1=accepted)"}; + const AxisSpec candidateSpecies{ + 4, -0.5, 3.5, "species (0=D0,1=D0bar,2=Dstar+,3=Dstar-)"}; + registry.add("events", "Events", kTH1F, {eventStage}); + registry.add("candidates", "Candidates", kTH1F, {candidateSpecies}); } template Date: Mon, 21 Sep 2026 22:13:28 +0200 Subject: [PATCH 16/23] Fix linter warining and format --- .../producerCharmHadronsCharmFemtoDream.cxx | 185 +++++----- .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 342 +++++++----------- 2 files changed, 214 insertions(+), 313 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 3575b194ccf..30093764ef8 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -1,6 +1,6 @@ -// Copyright 2019-2025 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright -// holders. All rights not expressly granted are reserved. +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. // // This software is distributed under the terms of the GNU General Public // License v3 (GPL Version 3), copied verbatim in the file "COPYING". @@ -26,6 +26,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include #include #include @@ -43,7 +44,6 @@ #include #include #include -#include #include #include @@ -100,18 +100,13 @@ struct HfProducerCharmHadronsCharmFemtoDream { using Collisions = soa::Join; using CollisionsWithFT0C = soa::Join; using D0s = soa::Join; - using Dstars = - soa::Join; + using Dstars = soa::Join; using D0sMl = soa::Join; - using DstarsMl = soa::Join; + using DstarsMl = soa::Join; - Filter filterSelectCandidateD0 = - (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || - aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); - Filter filterSelectCandidateDstar = - aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + Filter filterSelectCandidateD0 = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); + Filter filterSelectCandidateDstar = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; HistogramRegistry registry{"registry"}; OutputObj zorroSummary{"zorroSummary"}; @@ -121,9 +116,9 @@ struct HfProducerCharmHadronsCharmFemtoDream { int runNumber = -1; template - void initMl(HfMlConfig const&cfg, Response &response, bool withMl) { - if (cfg.applyMlMode.value < NoMl || - cfg.applyMlMode.value > FillMlFromNewBDT) { + void initMl(HfMlConfig const& cfg, Response& response, bool withMl) + { + if (cfg.applyMlMode.value < NoMl || cfg.applyMlMode.value > FillMlFromNewBDT) { LOGP(fatal, "{}: invalid applyMlMode", cfg.prefix); } if (cfg.applyMlMode.value != FillMlFromNewBDT) { @@ -132,31 +127,18 @@ struct HfProducerCharmHadronsCharmFemtoDream { if (!withMl) { LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); } - auto const&edges = cfg.binsPtMl.value; - if (edges.size() < NMlPtEdgesMin || - !std::all_of(edges.begin(), edges.end(), - [](double x) { return std::isfinite(x); }) || - std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { - return a >= b; - }) != edges.end()) { - LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", - cfg.prefix); + auto const& edges = cfg.binsPtMl.value; + if (edges.size() < NMlPtEdgesMin || !std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }) || std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { + return a >= b; + }) != edges.end()) { + LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", cfg.prefix); } const auto nBins = edges.size() - 1; - if (cfg.nClassesMl.value != NMlClasses || cfg.cutDirMl.value.size() != NMlClasses || - cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != NMlClasses || - cfg.onnxFileNames.value.size() != nBins || - cfg.namesInputFeatures.value.empty() || - (cfg.loadModelsFromCCDB.value && - cfg.modelPathsCCDB.value.size() != nBins)) { - LOGP(fatal, - "{}: provide three classes, cuts/models for every pT bin, and input " - "features", - cfg.prefix); + if (cfg.nClassesMl.value != NMlClasses || cfg.cutDirMl.value.size() != NMlClasses || cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != NMlClasses || cfg.onnxFileNames.value.size() != nBins || cfg.namesInputFeatures.value.empty() || (cfg.loadModelsFromCCDB.value && cfg.modelPathsCCDB.value.size() != nBins)) { + LOGP(fatal, "{}: provide three classes, cuts/models for every pT bin, and input features", cfg.prefix); } for (const auto& direction : cfg.cutDirMl.value) { - if (direction < o2::cuts_ml::CutGreater || - direction > o2::cuts_ml::CutNot) { + if (direction < o2::cuts_ml::CutGreater || direction > o2::cuts_ml::CutNot) { LOGP(fatal, "{}: invalid cutDirMl", cfg.prefix); } } @@ -167,30 +149,28 @@ struct HfProducerCharmHadronsCharmFemtoDream { } } } - response.configure(edges, cfg.cutsMl.value, cfg.cutDirMl.value, - cfg.nClassesMl.value); + response.configure(edges, cfg.cutsMl.value, cfg.cutDirMl.value, cfg.nClassesMl.value); response.cacheInputFeaturesIndices(cfg.namesInputFeatures.value); if (cfg.loadModelsFromCCDB.value) { ccdbApi.init(ccdbUrl.value); - response.setModelPathsCCDB(cfg.onnxFileNames.value, ccdbApi, - cfg.modelPathsCCDB.value, - cfg.timestampCCDB.value); + response.setModelPathsCCDB(cfg.onnxFileNames.value, ccdbApi, cfg.modelPathsCCDB.value, cfg.timestampCCDB.value); } else { response.setModelPathsLocal(cfg.onnxFileNames.value); } response.init(); } - void init(InitContext const&) { + void init(InitContext const&) + { const std::array processSwitches{ - static_cast(doprocessD0D0), - static_cast(doprocessD0Dstar), - static_cast(doprocessD0D0Ml), - static_cast(doprocessD0DstarMl), - static_cast(doprocessD0D0WithFT0C), - static_cast(doprocessD0DstarWithFT0C), - static_cast(doprocessD0D0MlWithFT0C), - static_cast(doprocessD0DstarMlWithFT0C)}; + static_cast(doprocessD0D0), + static_cast(doprocessD0Dstar), + static_cast(doprocessD0D0Ml), + static_cast(doprocessD0DstarMl), + static_cast(doprocessD0D0WithFT0C), + static_cast(doprocessD0DstarWithFT0C), + static_cast(doprocessD0D0MlWithFT0C), + static_cast(doprocessD0DstarMlWithFT0C)}; if (std::count(processSwitches.begin(), processSwitches.end(), true) != 1) { LOGP(fatal, "Enable exactly one charm-charm producer process"); } @@ -200,28 +180,25 @@ struct HfProducerCharmHadronsCharmFemtoDream { ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - const bool withMl = doprocessD0D0Ml || doprocessD0DstarMl || - doprocessD0D0MlWithFT0C || doprocessD0DstarMlWithFT0C; + const bool withMl = doprocessD0D0Ml || doprocessD0DstarMl || doprocessD0D0MlWithFT0C || doprocessD0DstarMlWithFT0C; initMl(mlD0, hfMlResponseD0, withMl); - if (doprocessD0Dstar || doprocessD0DstarMl || doprocessD0DstarWithFT0C || - doprocessD0DstarMlWithFT0C) { + if (doprocessD0Dstar || doprocessD0DstarMl || doprocessD0DstarWithFT0C || doprocessD0DstarMlWithFT0C) { initMl(mlDstar, hfMlResponseDstar, withMl); } hfEvSel.init(registry, &zorroSummary); const AxisSpec eventStage{2, -0.5, 1.5, "stage (0=all,1=accepted)"}; - const AxisSpec candidateSpecies{ - 4, -0.5, 3.5, "species (0=D0,1=D0bar,2=Dstar+,3=Dstar-)"}; + const AxisSpec candidateSpecies{4, -0.5, 3.5, "species (0=D0,1=D0bar,2=Dstar+,3=Dstar-)"}; registry.add("events", "Events", kTH1F, {eventStage}); registry.add("candidates", "Candidates", kTH1F, {candidateSpecies}); } template - bool acceptCollision(Collision const&col) { + bool acceptCollision(Collision const& col) + { registry.fill(HIST("events"), 0); float cent = -1.f; // No centrality for pp MB. - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask< - true, CentEstimator, aod::BCsWithTimestamps>(col, cent, ccdb, registry); + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(col, cent, ccdb, registry); hfEvSel.fillHistograms(col, rejectionMask, cent); if (rejectionMask != 0) { return false; @@ -231,8 +208,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { auto bc = col.template bc_as(); initCCDB(bc, runNumber, ccdb, ccdbPathGrpMag.value, nullptr, false); // Propagator field is in kG; FemtoDream uses Tesla. - const float fieldTesla = - 0.1f * o2::base::Propagator::Instance()->getNominalBz(); + const float fieldTesla = 0.1f * o2::base::Propagator::Instance()->getNominalBz(); // MultV0M is the common reduced percentile column: FT0C or -1 (no // estimator). collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, fieldTesla); @@ -242,10 +218,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - std::array readScores(Scores const&scores) { + std::array readScores(Scores const& scores) + { if (scores.size() != NMlClasses) { - LOGP(fatal, - "Expected three selector ML scores: background, prompt, nonprompt"); + LOGP(fatal, "Expected three selector ML scores: background, prompt, nonprompt"); return {-1.f, -1.f, -1.f}; } for (const auto& score : scores) { @@ -257,9 +233,9 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillD0(Collision const&col, Candidates const&candidates) { - const auto timestamp = - col.template bc_as().timestamp(); + void fillD0(Collision const& col, Candidates const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); for (const auto& cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); @@ -278,15 +254,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { } if (mlD0.applyMlMode == FillMlFromNewBDT) { // Do not call the ML response with an out-of-range model index. - if (!std::isfinite(cand.pt()) || - cand.pt() < mlD0.binsPtMl.value.front() || - cand.pt() >= mlD0.binsPtMl.value.back()) { + if (!std::isfinite(cand.pt()) || cand.pt() < mlD0.binsPtMl.value.front() || cand.pt() >= mlD0.binsPtMl.value.back()) { continue; } - const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 - : -o2::constants::physics::kD0; - auto features = - hfMlResponseD0.getInputFeatures(cand, pdgCode); + const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 : -o2::constants::physics::kD0; + auto features = hfMlResponseD0.getInputFeatures(cand, pdgCode); std::vector output; if (!hfMlResponseD0.isSelectedMl(features, cand.pt(), output)) { continue; // Reject this hypothesis only. @@ -305,9 +277,9 @@ struct HfProducerCharmHadronsCharmFemtoDream { } template - void fillDstar(Collision const&col, Candidates const&candidates) { - const auto timestamp = - col.template bc_as().timestamp(); + void fillDstar(Collision const& col, Candidates const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); for (const auto& cand : candidates) { auto p0 = cand.template prong0_as(); auto p1 = cand.template prong1_as(); @@ -318,9 +290,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { scores = readScores(cand.mlProbDstarToD0Pi()); } if (mlDstar.applyMlMode == FillMlFromNewBDT) { - if (!std::isfinite(cand.pt()) || - cand.pt() < mlDstar.binsPtMl.value.front() || - cand.pt() >= mlDstar.binsPtMl.value.back()) { + if (!std::isfinite(cand.pt()) || cand.pt() < mlDstar.binsPtMl.value.front() || cand.pt() >= mlDstar.binsPtMl.value.back()) { continue; } // Match the unswapped daughter convention used by D+track. @@ -341,9 +311,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } } - void processD0D0(Collisions::iterator const&col, + void processD0D0(Collisions::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s) { + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -351,10 +322,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { } PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); - void processD0Dstar(Collisions::iterator const&col, + void processD0Dstar(Collisions::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s, - soa::Filtered const&dstars) { + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -363,9 +335,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); - void processD0D0Ml(Collisions::iterator const&col, + void processD0D0Ml(Collisions::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s) { + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -373,10 +346,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { } PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); - void processD0DstarMl(Collisions::iterator const&col, + void processD0DstarMl(Collisions::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s, - soa::Filtered const&dstars) { + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -385,9 +359,10 @@ struct HfProducerCharmHadronsCharmFemtoDream { } PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); - void processD0D0WithFT0C(CollisionsWithFT0C::iterator const&col, + void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s) { + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -395,11 +370,12 @@ struct HfProducerCharmHadronsCharmFemtoDream { } PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, "D0 only, data with FT0C centrality", false); - void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const&col, + void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s, - soa::Filtered const&dstars) { + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -408,10 +384,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { } PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, "D0 and Dstar, data with FT0C centrality", false); - void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const&col, + void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s) { + soa::Filtered const& d0s) + { if (acceptCollision(col)) { fillD0(col, d0s); rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); @@ -419,11 +396,12 @@ struct HfProducerCharmHadronsCharmFemtoDream { } PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, "D0 with selector ML scores and FT0C centrality", false); - void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const&col, + void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const& col, aod::BCsWithTimestamps const&, aod::Tracks const&, - soa::Filtered const&d0s, - soa::Filtered const&dstars) { + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { if (acceptCollision(col)) { fillD0(col, d0s); fillDstar(col, dstars); @@ -433,7 +411,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMlWithFT0C, "D0 and Dstar with selector ML scores and FT0C centrality", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const&cfgc) { +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index 334ce094e73..a1b3142818e 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -1,6 +1,6 @@ -// Copyright 2019-2025 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright -// holders. All rights not expressly granted are reserved. +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. // // This software is distributed under the terms of the GNU General Public // License v3 (GPL Version 3), copied verbatim in the file "COPYING". @@ -68,77 +68,48 @@ struct HfTaskCharmHadronsCharmFemtoDream { Configurable ptMinDstar{"ptMinDstar", 0.f, "Minimum Dstar pT"}; Configurable ptMaxDstar{"ptMaxDstar", 36.f, "Maximum Dstar pT"}; Configurable etaMax{"etaMax", 0.8f, "Maximum absolute candidate eta"}; - Configurable massMinD0{"massMinD0", 1.7f, - "Minimum D0 mass (keep sidebands)"}; + Configurable massMinD0{"massMinD0", 1.7f, "Minimum D0 mass (keep sidebands)"}; Configurable massMaxD0{"massMaxD0", 2.0f, "Maximum D0 mass"}; - Configurable deltaMassMin{"deltaMassMin", MassPiPlus, - "Minimum Dstar-D0 mass difference"}; - Configurable deltaMassMax{"deltaMassMax", 0.17f, - "Maximum Dstar-D0 mass difference"}; - Configurable daughterMassMin{"daughterMassMin", 1.80f, - "Minimum Dstar daughter D0 mass"}; - Configurable daughterMassMax{"daughterMassMax", 1.93f, - "Maximum Dstar daughter D0 mass"}; - Configurable useMl{"useMl", false, - "Require valid ML scores and apply score cuts"}; + Configurable deltaMassMin{"deltaMassMin", MassPiPlus, "Minimum Dstar-D0 mass difference"}; + Configurable deltaMassMax{"deltaMassMax", 0.17f, "Maximum Dstar-D0 mass difference"}; + Configurable daughterMassMin{"daughterMassMin", 1.80f, "Minimum Dstar daughter D0 mass"}; + Configurable daughterMassMax{"daughterMassMax", 1.93f, "Maximum Dstar daughter D0 mass"}; + Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; Configurable maxBkgD0{"maxBkgD0", 1.f, "Maximum D0 background score"}; - Configurable minPromptD0{"minPromptD0", 0.f, - "Minimum D0 prompt score"}; - Configurable maxBkgDstar{"maxBkgDstar", 1.f, - "Maximum Dstar background score"}; - Configurable minPromptDstar{"minPromptDstar", 0.f, - "Minimum Dstar prompt score"}; + Configurable minPromptD0{"minPromptD0", 0.f, "Minimum D0 prompt score"}; + Configurable maxBkgDstar{"maxBkgDstar", 1.f, "Maximum Dstar background score"}; + Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { std::string prefix = "eventSel"; Configurable useCentrality{"useCentrality", false, "Apply percentile selection (requires a centrality-enabled producer)"}; Configurable multMin{"multMin", 0, "Minimum multiplicity (MultNtr)"}; - Configurable multMax{"multMax", 99999, - "Maximum multiplicity (MultNtr)"}; - Configurable multPercentileMin{"multPercentileMin", 0.f, - "Minimum multiplicity percentile"}; - Configurable multPercentileMax{"multPercentileMax", 100.f, - "Maximum multiplicity percentile"}; + Configurable multMax{"multMax", 99999, "Maximum multiplicity (MultNtr)"}; + Configurable multPercentileMin{"multPercentileMin", 0.f, "Minimum multiplicity percentile"}; + Configurable multPercentileMax{"multPercentileMax", 100.f, "Maximum multiplicity percentile"}; } eventSel; struct : ConfigurableGroup { Configurable doMixEvent{"doMixEvent", true, "Enable mixed events"}; Configurable mixingBinPolicy{"mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; - Configurable mixingDepth{"mixingDepth", 5, - "Number of neighbours for O2 event mixing"}; + Configurable mixingDepth{"mixingDepth", 5, "Number of neighbours for O2 event mixing"}; } mixSetting; SliceCache cache; - using FilteredCollisions = - soa::Filtered>; + using FilteredCollisions = soa::Filtered>; using FilteredCharmCand2Prongs = soa::Filtered; using FilteredCharmCandDstars = soa::Filtered; - Filter eventMultiplicity = - aod::femtodreamcollision::multNtr >= eventSel.multMin && - aod::femtodreamcollision::multNtr <= eventSel.multMax; - Filter eventMultiplicityPercentile = ifnode( - eventSel.useCentrality, - aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && - aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, - Node{LiteralNode{true}}); + Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= eventSel.multMin && aod::femtodreamcollision::multNtr <= eventSel.multMax; + Filter eventMultiplicityPercentile = ifnode(eventSel.useCentrality, aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, Node{LiteralNode{true}}); Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmHadCandSel; // Full-table process inputs do not automatically register grouping caches. // Register both keys explicitly for the sliceByCached calls below. - Preslice perCollisionD0 = - aod::femtodreamparticle::fdCollisionId; - Preslice perCollisionDstar = - aod::femtodreamparticle::fdCollisionId; + Preslice perCollisionD0 = aod::femtodreamparticle::fdCollisionId; + Preslice perCollisionDstar = aod::femtodreamparticle::fdCollisionId; - Partition partitionCharmHadron2Prong = ifnode( - useMl, - aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, - Node{LiteralNode{true}}); - Partition partitionCharmHadronDstar = - ifnode(useMl, - aod::fdhf::bdtBkg <= maxBkgDstar && - aod::fdhf::bdtPrompt >= minPromptDstar, - Node{LiteralNode{true}}); + Partition partitionCharmHadron2Prong = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, Node{LiteralNode{true}}); + Partition partitionCharmHadronDstar = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgDstar && aod::fdhf::bdtPrompt >= minPromptDstar, Node{LiteralNode{true}}); ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; @@ -149,7 +120,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; HistogramRegistry registry{"registry"}; - void init(InitContext const&) { + void init(InitContext const&) + { if (doprocessD0D0 == doprocessD0Dstar) { LOGP(fatal, "Enable exactly one charm-charm analysis process"); } @@ -165,44 +137,30 @@ struct HfTaskCharmHadronsCharmFemtoDream { colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; colBinningMultMultPercentile = { - {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; - const AxisSpec massD0{300, massMinD0.value, massMaxD0.value, - "M(Kpi) (GeV/c2)"}; - const AxisSpec deltaMass{310, deltaMassMin.value, deltaMassMax.value, - "Delta M (GeV/c2)"}; + const AxisSpec massD0{300, massMinD0.value, massMaxD0.value, "M(Kpi) (GeV/c2)"}; + const AxisSpec deltaMass{310, deltaMassMin.value, deltaMassMax.value, "Delta M (GeV/c2)"}; const AxisSpec pt{72, ptMinD0.value, ptMaxD0.value, "D0 pT (GeV/c)"}; - const AxisSpec ptStar{72, ptMinDstar.value, ptMaxDstar.value, - "Dstar pT (GeV/c)"}; + const AxisSpec ptStar{72, ptMinDstar.value, ptMaxDstar.value, "Dstar pT (GeV/c)"}; const AxisSpec mult{mixingBinMult, "NTracksPV"}; - const AxisSpec channel{static_cast(NPairChannels), -0.5, - static_cast(NPairChannels) - 0.5, - "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ " - "LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; - registry.add("SE/D0D0", "Same event", kTHnSparseF, - {kstar, massD0, massD0, pt, pt, mult, channel}); - registry.add("ME/D0D0", "Mixed event", kTHnSparseF, - {kstar, massD0, massD0, pt, pt, mult, channel}); - registry.add("SE/D0Dstar", "Same event", kTHnSparseF, - {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); - registry.add("ME/D0Dstar", "Mixed event", kTHnSparseF, - {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); - registry.add("QA/events", ";0=in mixing range,1=outside;events", kTH1F, - {{2, -0.5, 1.5}}); - registry.add("MixingQA/hSECollisionBins", ";mixing bin;events", kTH1F, - {{1000, -0.5, 999.5}}); - registry.add("MixingQA/hMECollisionBinsD0D0", ";mixing bin;event pairs", - kTH1F, {{1000, -0.5, 999.5}}); - registry.add("MixingQA/hMECollisionBinsD0Dstar", ";mixing bin;event pairs", - kTH1F, {{1000, -0.5, 999.5}}); - registry.add("QA/sharedDaughters", ";0=D0D0,1=D0Dstar;rejected pairs", - kTH1F, {{2, -0.5, 1.5}}); + const AxisSpec channel{static_cast(NPairChannels), -0.5, static_cast(NPairChannels) - 0.5, "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; + registry.add("SE/D0D0", "Same event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("ME/D0D0", "Mixed event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("SE/D0Dstar", "Same event", kTHnSparseF, {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("ME/D0Dstar", "Mixed event", kTHnSparseF, {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("QA/events", ";0=in mixing range,1=outside;events", kTH1F, {{2, -0.5, 1.5}}); + registry.add("MixingQA/hSECollisionBins", ";mixing bin;events", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0D0", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0Dstar", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("QA/sharedDaughters", ";0=D0D0,1=D0Dstar;rejected pairs", kTH1F, {{2, -0.5, 1.5}}); registry.add("QA/d0MassPt", ";M(Kpi);pT", kTH2F, {massD0, pt}); registry.add("QA/dstarMassPt", ";Delta M;pT", kTH2F, {deltaMass, ptStar}); } template - static bool sharesDaughter(D0Row const&a, OtherRow const&b) { + static bool sharesDaughter(D0Row const& a, OtherRow const& b) + { for (const auto& id : std::array{a.prong0Id(), a.prong1Id()}) { if (id < 0) { continue; @@ -221,22 +179,19 @@ struct HfTaskCharmHadronsCharmFemtoDream { // D0 charge denotes flavour, not electric charge. template - static PairChannel pairChannel(D0Row const&d0, OtherRow const&other) { + static PairChannel pairChannel(D0Row const& d0, OtherRow const& other) + { if constexpr (!IsDstar) { - return d0.charge() != other.charge() - ? D0D0barUnlikeSign - : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); + return d0.charge() != other.charge() ? D0D0barUnlikeSign : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); } - return d0.charge() > 0 ? (other.charge() > 0 ? D0DstarPlusLikeSign - : D0DstarMinusUnlikeSign) - : (other.charge() < 0 ? D0barDstarMinusLikeSign - : D0barDstarPlusUnlikeSign); + return d0.charge() > 0 ? (other.charge() > 0 ? D0DstarPlusLikeSign : D0DstarMinusUnlikeSign) : (other.charge() < 0 ? D0barDstarMinusLikeSign : D0barDstarPlusUnlikeSign); } // Canonical ordering keeps mass/pt axes independent of event ordering for // D0D0. template - static bool reverseD0Order(FirstRow const&first, SecondRow const&second) { + static bool reverseD0Order(FirstRow const& first, SecondRow const& second) + { if (first.charge() != second.charge()) { return first.charge() < second.charge(); // D0 first for D0-D0bar } @@ -244,44 +199,36 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void validateMlScores(Candidates const&candidates) { + void validateMlScores(Candidates const& candidates) + { if (!useMl) { return; } for (const auto& row : candidates) { if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { - LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML " - "producer process"); + LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML producer process"); } } } template - bool select(Row const&row, float &mass) { - if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || - !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || - std::abs(row.eta()) >= etaMax || - row.pt() < (IsDstar ? ptMinDstar.value : ptMinD0.value) || - row.pt() >= (IsDstar ? ptMaxDstar.value : ptMaxD0.value)) { + bool select(Row const& row, float& mass) + { + if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || std::abs(row.eta()) >= etaMax || row.pt() < (IsDstar ? ptMinDstar.value : ptMinD0.value) || row.pt() >= (IsDstar ? ptMaxDstar.value : ptMaxD0.value)) { return false; } - const std::array masses = - row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} - : std::array{MassKPlus, MassPiPlus}; + const std::array masses = row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; if constexpr (IsDstar) { const float daughterMass = row.mDaughD0(masses); - if (!std::isfinite(daughterMass) || daughterMass < daughterMassMin || - daughterMass >= daughterMassMax) { + if (!std::isfinite(daughterMass) || daughterMass < daughterMassMin || daughterMass >= daughterMassMax) { return false; } mass = row.m(std::array{masses[0], masses[1], MassPiPlus}) - daughterMass; } else { mass = row.m(masses); } - if (!std::isfinite(mass) || - mass < (IsDstar ? deltaMassMin.value : massMinD0.value) || - mass >= (IsDstar ? deltaMassMax.value : massMaxD0.value)) { + if (!std::isfinite(mass) || mass < (IsDstar ? deltaMassMin.value : massMinD0.value) || mass >= (IsDstar ? deltaMassMax.value : massMaxD0.value)) { return false; } if constexpr (IsDstar) { @@ -297,8 +244,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void fillPair(FirstRow const&first, SecondRow const&second, float firstMass, - float secondMass, float mult) { + void fillPair(FirstRow const& first, SecondRow const& second, float firstMass, + float secondMass, float mult) + { if constexpr (!Mixed) { if (sharesDaughter(first, second)) { registry.fill(HIST("QA/sharedDaughters"), IsDstar ? 1 : 0); @@ -312,57 +260,46 @@ struct HfTaskCharmHadronsCharmFemtoDream { const float pt2 = reverse ? first.pt() : second.pt(); // Read kinematics directly from the candidate rows. Only D0D0 can reverse; // its two nominal parent masses are equal. Never use Dstar delta mass here. - const float kstar = - reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) - : FemtoDreamMath::getkstar(first, MassD0, second, - IsDstar ? MassDStar : MassD0); + const float kstar = reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) : FemtoDreamMath::getkstar(first, MassD0, second, IsDstar ? MassDStar : MassD0); if (!std::isfinite(kstar)) { return; } const int channel = pairChannel(first, second); if constexpr (IsDstar && Mixed) { - registry.fill(HIST("ME/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, - channel); + registry.fill(HIST("ME/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); } else if constexpr (IsDstar) { - registry.fill(HIST("SE/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, - channel); + registry.fill(HIST("SE/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); } else if constexpr (Mixed) { - registry.fill(HIST("ME/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, - channel); + registry.fill(HIST("ME/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); } else { - registry.fill(HIST("SE/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, - channel); + registry.fill(HIST("SE/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); } } template - int mixingBin(Policy const&policy, Collision col) { + int mixingBin(Policy const& policy, Collision col) + { return policy.getBin(policy.getBinningValues(col)); } - template - void doMixedEvent(FilteredCollisions const&cols, D0Table const&d0Rows, - OtherTable const&otherRows, Policy const&policy) { - Partition partitionMaskedCol1 = - (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + template + void doMixedEvent(FilteredCollisions const& cols, + D0Table const& d0Rows, + OtherTable const& otherRows, + Policy const& policy) + { + Partition partitionMaskedCol1 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. - Partition partitionMaskedCol2 = - (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; - Partition partitionMaskedColDstar = - (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; + Partition partitionMaskedCol2 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + Partition partitionMaskedColDstar = (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; partitionMaskedCol1.bindTable(cols); if constexpr (IsDstar) { partitionMaskedColDstar.bindTable(cols); } else { partitionMaskedCol2.bindTable(cols); } - auto const&secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered - : *partitionMaskedCol2.mFiltered; - for (const auto& [collision1, collision2] : - combinations(soa::CombinationsBlockFullIndexPolicy( - policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, - secondCollisions))) { + auto const& secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered : *partitionMaskedCol2.mFiltered; + for (const auto& [collision1, collision2] : combinations(soa::CombinationsBlockFullIndexPolicy(policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, secondCollisions))) { if (collision1.globalIndex() == collision2.globalIndex()) { continue; } @@ -373,30 +310,22 @@ struct HfTaskCharmHadronsCharmFemtoDream { continue; } } - if (!std::isfinite(collision1.magField()) || - collision1.magField() != collision2.magField()) { + if (!std::isfinite(collision1.magField()) || collision1.magField() != collision2.magField()) { continue; } if constexpr (IsDstar) { - registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), - mixingBin(policy, collision1)); + registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), mixingBin(policy, collision1)); } else { - registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), - mixingBin(policy, collision1)); + registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), mixingBin(policy, collision1)); } - auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, - collision1.globalIndex(), cache); - auto second = - otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, - collision2.globalIndex(), cache); + auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); + auto second = otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision2.globalIndex(), cache); // Species roles remain fixed; the full policy supplies the reverse event // orientation when eligible. Do not add a second manual reverse loop. - for (const auto& [row1, row2] : - combinations(soa::CombinationsFullIndexPolicy(first, second))) { + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(first, second))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { - fillPair(row1, row2, mass1, mass2, - collision1.multNtr()); + fillPair(row1, row2, mass1, mass2, collision1.multNtr()); } } } @@ -404,10 +333,11 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Write candidates once per collision, independently of SE/ME pair counts. // Reuse the same output tables and field conventions as the track/V0 tasks. - template - void fillTables(CollType const&col, D0Slice const&d0s, - DstarSlice const&dstars) { + template + void fillTables(CollType const& col, + D0Slice const& d0s, + DstarSlice const& dstars) + { int64_t timeStamp = -1; bool hasCandidate = false; auto recordTimeStamp = [&](int64_t value) { @@ -435,37 +365,33 @@ struct HfTaskCharmHadronsCharmFemtoDream { continue; } recordTimeStamp(part.timeStamp()); - const std::array daughterMasses = - part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} - : std::array{MassKPlus, MassPiPlus}; + const std::array daughterMasses = part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. rowFemtoResultCharmDstar( - col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), - part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), - part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD(), 0, 0); + col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), + part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), + part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), + part.bdtFD(), 0, 0); } } // Retain D0-only/Dstar-only events even when no clean SE pair exists. // MC fields above are zero for these data processes, as in existing tasks. if (hasCandidate) { - rowFemtoResultColl(col.globalIndex(), timeStamp, col.posZ(), - col.multNtr()); + rowFemtoResultColl(col.globalIndex(), timeStamp, col.posZ(), col.multNtr()); } } - template - void runWithPolicy(FilteredCollisions const&cols, D0Table const&d0Rows, - DstarTable const&dstarRows, Policy const&policy) { + template + void runWithPolicy(FilteredCollisions const& cols, + D0Table const& d0Rows, + DstarTable const& dstarRows, + Policy const& policy) + { for (const auto& col : cols) { - auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, - col.globalIndex(), cache); - // Export before online mixing-bin cuts, so offline mixing can change - // bins. + auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + // Export before online mixing-bin cuts, so offline mixing can change bins. if constexpr (WithDstar) { - auto dstars = dstarRows.sliceByCached( - aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); fillTables(col, d0s, dstars); } else { fillTables(col, d0s, d0s); @@ -477,18 +403,15 @@ struct HfTaskCharmHadronsCharmFemtoDream { } registry.fill(HIST("QA/events"), 0); registry.fill(HIST("MixingQA/hSECollisionBins"), bin); - for (const auto& [row1, row2] : - combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { + for (const auto& [row1, row2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { fillPair(row1, row2, mass1, mass2, col.multNtr()); } } if constexpr (WithDstar) { - auto dstars = dstarRows.sliceByCached( - aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); - for (const auto& [row1, row2] : - combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { float mass1{}, mass2{}; if (select(row1, mass1) && select(row2, mass2)) { fillPair(row1, row2, mass1, mass2, col.multNtr()); @@ -505,9 +428,10 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void run(FilteredCollisions const&cols, - FilteredCharmCand2Prongs const&d0Rows, - DstarTable const&dstarRows) { + void run(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0Rows, + DstarTable const& dstarRows) + { validateMlScores(d0Rows); partitionCharmHadron2Prong.bindTable(d0Rows); if constexpr (WithDstar) { @@ -515,40 +439,38 @@ struct HfTaskCharmHadronsCharmFemtoDream { partitionCharmHadronDstar.bindTable(dstarRows); } switch (mixSetting.mixingBinPolicy) { - case aod::femtodreamcollision::kMult: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, colBinningMult); - break; - case aod::femtodreamcollision::kMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, - colBinningMultPercentile); - break; - case aod::femtodreamcollision::kMultMultPercentile: - runWithPolicy(cols, partitionCharmHadron2Prong, - partitionCharmHadronDstar, - colBinningMultMultPercentile); - break; - default: - LOGP(fatal, "Invalid mixing binning policy"); + case aod::femtodreamcollision::kMult: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMult); + break; + case aod::femtodreamcollision::kMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultPercentile); + break; + case aod::femtodreamcollision::kMultMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultMultPercentile); + break; + default: + LOGP(fatal, "Invalid mixing binning policy"); } } - void processD0D0(FilteredCollisions const&cols, - FilteredCharmCand2Prongs const&d0s) { + void processD0D0(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s) + { run(cols, d0s, d0s); } PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, "D0D0 data, SE and ME", true); - void processD0Dstar(FilteredCollisions const&cols, - FilteredCharmCand2Prongs const&d0s, - FilteredCharmCandDstars const&dstars) { + void processD0Dstar(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s, + FilteredCharmCandDstars const& dstars) + { run(cols, d0s, dstars); } PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, "D0D0 and D0Dstar data, SE and ME", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const&cfgc) { +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } From 59e55b23e4ed1260b9e8231e108126ce9cb129b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:18:36 +0200 Subject: [PATCH 17/23] Fix function signatures in producerCharmHadrons --- .../TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 30093764ef8..a721351918b 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -347,7 +347,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); void processD0DstarMl(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, + aod::BCsWithTimestamps const&, + aod::Tracks const&, soa::Filtered const& d0s, soa::Filtered const& dstars) { @@ -360,7 +361,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, + aod::BCsWithTimestamps const&, + aod::Tracks const&, soa::Filtered const& d0s) { if (acceptCollision(col)) { From df3661b57675919610ae576ed28f28a46dd9fa37 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 21 Sep 2026 20:19:22 +0000 Subject: [PATCH 18/23] Please consider the following formatting changes --- PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index a721351918b..ba03a07786d 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -26,7 +26,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include +#include "Tools/ML/MlResponse.h" #include #include From c3a61da9208102aa1a52fc334dd0237f904797d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Tue, 22 Sep 2026 09:48:43 +0200 Subject: [PATCH 19/23] Refactor edge validation logic for clarity --- .../TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index ba03a07786d..11358bdc61d 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -128,9 +128,9 @@ struct HfProducerCharmHadronsCharmFemtoDream { LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); } auto const& edges = cfg.binsPtMl.value; - if (edges.size() < NMlPtEdgesMin || !std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }) || std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { - return a >= b; - }) != edges.end()) { + const bool areEdgesFinite = std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }); + const bool areEdgesIncreasing = std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { return a >= b; }) == edges.end(); + if (edges.size() < NMlPtEdgesMin || !areEdgesFinite || !areEdgesIncreasing) { LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", cfg.prefix); } const auto nBins = edges.size() - 1; From c37cf9d35f099fe31eefecb4035f544d7e25c399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Tue, 22 Sep 2026 11:00:47 +0200 Subject: [PATCH 20/23] Refactor charm hadron task parameters and methods --- .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 86 ++++++++----------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index a1b3142818e..5b2a3861997 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -75,10 +75,10 @@ struct HfTaskCharmHadronsCharmFemtoDream { Configurable daughterMassMin{"daughterMassMin", 1.80f, "Minimum Dstar daughter D0 mass"}; Configurable daughterMassMax{"daughterMassMax", 1.93f, "Maximum Dstar daughter D0 mass"}; Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; - Configurable maxBkgD0{"maxBkgD0", 1.f, "Maximum D0 background score"}; - Configurable minPromptD0{"minPromptD0", 0.f, "Minimum D0 prompt score"}; - Configurable maxBkgDstar{"maxBkgDstar", 1.f, "Maximum Dstar background score"}; - Configurable minPromptDstar{"minPromptDstar", 0.f, "Minimum Dstar prompt score"}; + Configurable d0BkgBdtMax{"d0BkgBdtMax", 1.f, "Maximum D0 background score"}; + Configurable d0PromptBdtMin{"d0PromptBdtMin", 0.f, "Minimum D0 prompt score"}; + Configurable dstarBkgBdtMax{"dstarBkgBdtMax", 1.f, "Maximum Dstar background score"}; + Configurable dstarPromptBdtMin{"dstarPromptBdtMin", 0.f, "Minimum Dstar prompt score"}; Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; struct : ConfigurableGroup { std::string prefix = "eventSel"; @@ -108,8 +108,8 @@ struct HfTaskCharmHadronsCharmFemtoDream { Preslice perCollisionD0 = aod::femtodreamparticle::fdCollisionId; Preslice perCollisionDstar = aod::femtodreamparticle::fdCollisionId; - Partition partitionCharmHadron2Prong = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgD0 && aod::fdhf::bdtPrompt >= minPromptD0, Node{LiteralNode{true}}); - Partition partitionCharmHadronDstar = ifnode(useMl, aod::fdhf::bdtBkg <= maxBkgDstar && aod::fdhf::bdtPrompt >= minPromptDstar, Node{LiteralNode{true}}); + Partition partitionCharmHadron2Prong = ifnode(useMl, aod::fdhf::bdtBkg <= d0BkgBdtMax && aod::fdhf::bdtPrompt >= d0PromptBdtMin, Node{LiteralNode{true}}); + Partition partitionCharmHadronDstar = ifnode(useMl, aod::fdhf::bdtBkg <= dstarBkgBdtMax && aod::fdhf::bdtPrompt >= dstarPromptBdtMin, Node{LiteralNode{true}}); ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; @@ -125,19 +125,12 @@ struct HfTaskCharmHadronsCharmFemtoDream { if (doprocessD0D0 == doprocessD0Dstar) { LOGP(fatal, "Enable exactly one charm-charm analysis process"); } - if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || - mixSetting.mixingBinPolicy > MixingBinPolicyMax || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || - ptMinDstar < 0 || ptMinDstar >= ptMaxDstar || etaMax <= 0 || - massMinD0 >= massMaxD0 || deltaMassMin >= deltaMassMax || - daughterMassMin >= daughterMassMax || charmHadCandSel < 1 || - eventSel.multMin > eventSel.multMax || - eventSel.multPercentileMin > eventSel.multPercentileMax) { + if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > MixingBinPolicyMax || ptMinD0 < 0 || ptMinD0 >= ptMaxD0 || ptMinDstar < 0 || ptMinDstar >= ptMaxDstar || etaMax <= 0 || massMinD0 >= massMaxD0 || deltaMassMin >= deltaMassMax || daughterMassMin >= daughterMassMax || charmHadCandSel < 1 || eventSel.multMin > eventSel.multMax || eventSel.multPercentileMin > eventSel.multPercentileMax) { LOGP(fatal, "Invalid charm-charm selection or mixing configuration"); } colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = { - {mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; const AxisSpec massD0{300, massMinD0.value, massMaxD0.value, "M(Kpi) (GeV/c2)"}; const AxisSpec deltaMass{310, deltaMassMin.value, deltaMassMax.value, "Delta M (GeV/c2)"}; @@ -198,15 +191,14 @@ struct HfTaskCharmHadronsCharmFemtoDream { return first.pt() < second.pt(); // leading pT first for equal flavours } - template - void validateMlScores(Candidates const& candidates) + template + void validateMlScores(CandsType const& candidates) { if (!useMl) { return; } for (const auto& row : candidates) { - if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || - row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { + if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML producer process"); } } @@ -244,8 +236,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void fillPair(FirstRow const& first, SecondRow const& second, float firstMass, - float secondMass, float mult) + void fillPair(FirstRow const& first, SecondRow const& second, float firstMass, float secondMass, float mult) { if constexpr (!Mixed) { if (sharesDaughter(first, second)) { @@ -276,17 +267,23 @@ struct HfTaskCharmHadronsCharmFemtoDream { } } - template - int mixingBin(Policy const& policy, Collision col) + template + int mixingBin(CollType const& col) { - return policy.getBin(policy.getBinningValues(col)); + switch (mixSetting.mixingBinPolicy) { + case aod::femtodreamcollision::kMult: + return colBinningMult.getBin({col.posZ(), col.multNtr()}); + case aod::femtodreamcollision::kMultPercentile: + return colBinningMultPercentile.getBin({col.posZ(), col.multV0M()}); + case aod::femtodreamcollision::kMultMultPercentile: + return colBinningMultMultPercentile.getBin({col.posZ(), col.multNtr(), col.multV0M()}); + default: + return -1; + } } template - void doMixedEvent(FilteredCollisions const& cols, - D0Table const& d0Rows, - OtherTable const& otherRows, - Policy const& policy) + void doMixedEvent(FilteredCollisions const& cols, D0Table const& d0Rows, OtherTable const& otherRows, Policy const& policy) { Partition partitionMaskedCol1 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. @@ -314,9 +311,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { continue; } if constexpr (IsDstar) { - registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), mixingBin(policy, collision1)); + registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), mixingBin(collision1)); } else { - registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), mixingBin(policy, collision1)); + registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), mixingBin(collision1)); } auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); auto second = otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision2.globalIndex(), cache); @@ -334,9 +331,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { // Write candidates once per collision, independently of SE/ME pair counts. // Reuse the same output tables and field conventions as the track/V0 tasks. template - void fillTables(CollType const& col, - D0Slice const& d0s, - DstarSlice const& dstars) + void fillTables(CollType const& col, D0Slice const& d0s, DstarSlice const& dstars) { int64_t timeStamp = -1; bool hasCandidate = false; @@ -353,10 +348,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { continue; } recordTimeStamp(part.timeStamp()); - rowFemtoResultCharm2Prong(col.globalIndex(), timeStamp, mass, part.pt(), - part.eta(), part.phi(), part.prong0Id(), - part.prong1Id(), part.charge(), part.bdtBkg(), - part.bdtPrompt(), part.bdtFD(), 0, 0); + rowFemtoResultCharm2Prong(col.globalIndex(), timeStamp, mass, part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); } if constexpr (WithDstar) { for (const auto& part : dstars) { @@ -367,11 +359,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { recordTimeStamp(part.timeStamp()); const std::array daughterMasses = part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. - rowFemtoResultCharmDstar( - col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), - part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), - part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD(), 0, 0); + rowFemtoResultCharmDstar(col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); } } // Retain D0-only/Dstar-only events even when no clean SE pair exists. @@ -382,10 +370,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void runWithPolicy(FilteredCollisions const& cols, - D0Table const& d0Rows, - DstarTable const& dstarRows, - Policy const& policy) + void runWithPolicy(FilteredCollisions const& cols, D0Table const& d0Rows, DstarTable const& dstarRows, Policy const& policy) { for (const auto& col : cols) { auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); @@ -396,7 +381,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { } else { fillTables(col, d0s, d0s); } - const int bin = mixingBin(policy, col); + const int bin = mixingBin(col); if (bin < 0 || !std::isfinite(col.magField())) { registry.fill(HIST("QA/events"), 1); continue; @@ -428,9 +413,7 @@ struct HfTaskCharmHadronsCharmFemtoDream { } template - void run(FilteredCollisions const& cols, - FilteredCharmCand2Prongs const& d0Rows, - DstarTable const& dstarRows) + void run(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0Rows, DstarTable const& dstarRows) { validateMlScores(d0Rows); partitionCharmHadron2Prong.bindTable(d0Rows); @@ -471,6 +454,5 @@ struct HfTaskCharmHadronsCharmFemtoDream { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From 0fd7d0a2e146953a47fec701097ca0e498dc5a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Tue, 22 Sep 2026 11:04:14 +0200 Subject: [PATCH 21/23] Refactor processSwitches initialization and template parameters --- .../producerCharmHadronsCharmFemtoDream.cxx | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index 11358bdc61d..e00ac29dbb0 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -162,15 +162,7 @@ struct HfProducerCharmHadronsCharmFemtoDream { void init(InitContext const&) { - const std::array processSwitches{ - static_cast(doprocessD0D0), - static_cast(doprocessD0Dstar), - static_cast(doprocessD0D0Ml), - static_cast(doprocessD0DstarMl), - static_cast(doprocessD0D0WithFT0C), - static_cast(doprocessD0DstarWithFT0C), - static_cast(doprocessD0D0MlWithFT0C), - static_cast(doprocessD0DstarMlWithFT0C)}; + const std::array processSwitches{doprocessD0D0, doprocessD0Dstar, doprocessD0D0Ml, doprocessD0DstarMl, doprocessD0D0WithFT0C, doprocessD0DstarWithFT0C, doprocessD0D0MlWithFT0C, doprocessD0DstarMlWithFT0C}; if (std::count(processSwitches.begin(), processSwitches.end(), true) != 1) { LOGP(fatal, "Enable exactly one charm-charm producer process"); } @@ -192,9 +184,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { registry.add("candidates", "Candidates", kTH1F, {candidateSpecies}); } - template - bool acceptCollision(Collision const& col) + template + bool acceptCollision(CollType const& col) { registry.fill(HIST("events"), 0); float cent = -1.f; // No centrality for pp MB. @@ -232,8 +223,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { return {scores[0], scores[1], scores[2]}; } - template - void fillD0(Collision const& col, Candidates const& candidates) + template + void fillD0(CollType const& col, CandsType const& candidates) { const auto timestamp = col.template bc_as().timestamp(); for (const auto& cand : candidates) { @@ -242,15 +233,13 @@ struct HfProducerCharmHadronsCharmFemtoDream { // The OR filter accepts the row if either hypothesis passes. Write only // the passing hypotheses, each with its own flavour and ML scores. for (int hypothesis = 0; hypothesis < ND0Hypotheses; ++hypothesis) { - if ((hypothesis == 0 ? cand.isSelD0() : cand.isSelD0bar()) < - selectionFlagD0) { + if ((hypothesis == 0 ? cand.isSelD0() : cand.isSelD0bar()) < selectionFlagD0) { continue; } std::array scores{-1.f, -1.f, -1.f}; if constexpr (WithMl) { if (mlD0.applyMlMode != NoMl) { - scores = hypothesis == 0 ? readScores(cand.mlProbD0()) - : readScores(cand.mlProbD0bar()); + scores = hypothesis == 0 ? readScores(cand.mlProbD0()) : readScores(cand.mlProbD0bar()); } if (mlD0.applyMlMode == FillMlFromNewBDT) { // Do not call the ML response with an out-of-range model index. @@ -266,18 +255,15 @@ struct HfProducerCharmHadronsCharmFemtoDream { scores = readScores(output); } } - d0Rows(collisions.lastIndex(), timestamp, hypothesis == 0 ? 1 : -1, - p0.globalIndex(), p1.globalIndex(), p0.pt(), p1.pt(), p0.eta(), - p1.eta(), p0.phi(), p1.phi(), 1 << hypothesis, scores[0], - scores[1], scores[2]); + d0Rows(collisions.lastIndex(), timestamp, hypothesis == 0 ? 1 : -1, p0.globalIndex(), p1.globalIndex(), p0.pt(), p1.pt(), p0.eta(), p1.eta(), p0.phi(), p1.phi(), 1 << hypothesis, scores[0], scores[1], scores[2]); hasD0 = true; registry.fill(HIST("candidates"), hypothesis); } } } - template - void fillDstar(Collision const& col, Candidates const& candidates) + template + void fillDstar(CollType const& col, CandsType const& candidates) { const auto timestamp = col.template bc_as().timestamp(); for (const auto& cand : candidates) { @@ -302,17 +288,15 @@ struct HfProducerCharmHadronsCharmFemtoDream { scores = readScores(output); } } - dstarRows(collisions.lastIndex(), timestamp, soft.sign(), - p0.globalIndex(), p1.globalIndex(), soft.globalIndex(), p0.pt(), - p1.pt(), soft.pt(), p0.eta(), p1.eta(), soft.eta(), p0.phi(), - p1.phi(), soft.phi(), 1, scores[0], scores[1], scores[2]); + dstarRows(collisions.lastIndex(), timestamp, soft.sign(), p0.globalIndex(), p1.globalIndex(), soft.globalIndex(), p0.pt(), p1.pt(), soft.pt(), p0.eta(), p1.eta(), soft.eta(), p0.phi(), p1.phi(), soft.phi(), 1, scores[0], scores[1], scores[2]); hasDstar = true; registry.fill(HIST("candidates"), soft.sign() > 0 ? 2 : 3); } } void processD0D0(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, + aod::BCsWithTimestamps const&, + aod::Tracks const&, soa::Filtered const& d0s) { if (acceptCollision(col)) { @@ -323,7 +307,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); void processD0Dstar(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, + aod::BCsWithTimestamps const&, + aod::Tracks const&, soa::Filtered const& d0s, soa::Filtered const& dstars) { @@ -336,7 +321,8 @@ struct HfProducerCharmHadronsCharmFemtoDream { PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); void processD0D0Ml(Collisions::iterator const& col, - aod::BCsWithTimestamps const&, aod::Tracks const&, + aod::BCsWithTimestamps const&, + aod::Tracks const&, soa::Filtered const& d0s) { if (acceptCollision(col)) { @@ -415,6 +401,5 @@ struct HfProducerCharmHadronsCharmFemtoDream { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From 9c3f4488f2f7ebbbd6ca6b4a001aa410a1df6c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:33:10 +0200 Subject: [PATCH 22/23] Remove OutputObjHeader include and add cstddef --- PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index e00ac29dbb0..3a58131ad6a 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -42,12 +42,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include From 3154a04a3cf32c9cacbce138cd3131e68ecf4db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:33:41 +0200 Subject: [PATCH 23/23] Remove unused include for OutputObjHeader Removed unused OutputObjHeader include. --- PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index 5b2a3861997..f051d75cf27 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -28,7 +28,6 @@ #include #include #include -#include #include #include