From 90eb7190262a0e4dda20384305d2ed8f4813e27e Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 10:51:00 +0200 Subject: [PATCH 01/10] [PWGHF] Add task for H2 from Lb analysis --- PWGHF/D2H/Tasks/CMakeLists.txt | 8 + PWGHF/D2H/Tasks/taskH2fromLb.cxx | 331 +++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 PWGHF/D2H/Tasks/taskH2fromLb.cxx diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 96296bf2c6b..cd9cd149dc8 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -168,3 +168,11 @@ o2physics_add_dpl_workflow(task-hidden-charm SOURCES taskHiddenCharm.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(task-h2-from-lb + SOURCES taskH2fromLb.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + + + diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2fromLb.cxx new file mode 100644 index 00000000000..fe801b8c4cc --- /dev/null +++ b/PWGHF/D2H/Tasks/taskH2fromLb.cxx @@ -0,0 +1,331 @@ +// 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". +// +// 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. +// +/// \brief A filter task for non prompt deuterons +/// \author Marta Razza marta.razza@cern.ch +/// \author Francesca Ercolessi francesca.ercolessi@cern.ch +/// \since Dec 17, 2025 + +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#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 H2fromLb { + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + + // Define a histograms and registries + HistogramRegistry QAHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + OutputObj hProcessedEvents{TH1D("hProcessedEvents", "Event filtered;; Number of events", 4, 0., 4.)}; + + Configurable cutzvertex{"cutzvertex", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable applySkimming{"applySkimming", false, "Skimmed dataset processing"}; + Configurable cfgSkimming{"cfgSkimming", "fH2fromLb", "Configurable for skimming"}; + Configurable sel8{"sel8", true, "sel8 event selection"}; + Configurable separateAntideuterons{"separateAntideuterons", true, "Fill FromLb histos for primary deuterons whose mother is Lb, Primary histos for all others"}; + Configurable cfgEta{"cfgEta", 0.8f, "Track eta selection"}; + Configurable cfgTPCNclsFound{"cfgTPCNclsFound", 100, "Minimum TPC clusters found"}; + Configurable cfgTPCChi2Ncl{"cfgTPCChi2Ncl", 4.0f, "Maximum TPC chi2 per N clusters"}; + Configurable cfgITSChi2Ncl{"cfgITSChi2Ncl", 36.0f, "Maximum ITS chi2 per N clusters"}; + Configurable cfgITScls{"cfgITScls", 2, "Minimum ITS clusters"}; + Configurable cfgMaxPt{"cfgMaxPt", 5.0f, "Maximum pT cut"}; + Configurable cfgMinPt{"cfgMinPt", 0.5f, "Minimum pT cut"}; + Configurable cfgTPCNsigma{"cfgTPCNsigma", 4.0f, "TPC n sigma for deuteron PID"}; + Configurable cfgTOFNsigma_min{"cfgTOFNsigma_min", 3.0f, "TOF n sigma min for deuteron PID"}; + Configurable cfgTOFNsigma_max{"cfgTOFNsigma_max", 4.0f, "TOF n sigma max for deuteron PID"}; + Configurable ptThresholdPid{"ptThresholdPid", 1.0f, "pT threshold to switch between 4 and 3 sigmas for TOF PID"}; + // PDG codes + Configurable pdgCodeMother{"pdgCodeMother", -5122, "PDG code of the mother particle (default: anti-Lambda_b)"}; + Configurable pdgCodeDaughter{"pdgCodeDaughter", -1000010020, "PDG code of the daughter particle (default: anti-deuteron)"}; + + int mRunNumber = 0; + float d_bz = 0.f; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + framework::Service ccdb; + void init(framework::InitContext&) + { + + ccdb->setURL("http://alice-ccdb.cern.ch"); // Set CCDB URL to get magnetic field + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + if (applySkimming) { + zorroSummary.setObject(zorro.getZorroSummary()); + } + + ConfigurableAxis ptAxis{"ptAxis", {100, 0., 10.f}, "p_{T} GeV/c"}; + ConfigurableAxis nSigmaAxis{"nSigmaAxis", {200, -10.f, 10.f}, "nSigma"}; + ConfigurableAxis DCAxyAxis{"DCAxyAxis", {1000, -0.2f, 0.2f}, "DCA xy (cm)"}; + ConfigurableAxis DCAzAxis{"DCAzAxis", {1000, -0.2f, 0.2f}, "DCA z (cm)"}; + + // general QA histograms + QAHistos.add("hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); + QAHistos.add("ptGeneratedLb", "ptGeneratedLb", HistType::kTH1F, {ptAxis}); + QAHistos.add("ptAntiDeuteronPrimary", "ptAntiDeuteronPrimaryReco", HistType::kTH1F, {ptAxis}); + QAHistos.add("ptAntiDeuteronFromLb", "ptAntiDeuteronFromLbReco", HistType::kTH1F, {ptAxis}); + QAHistos.add("hDCAxy-Primary", "DCAxy-Primary", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); + QAHistos.add("hDCAxy-FromLb", "DCAxy-FromLb", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); + QAHistos.add("hDCAxyVsPt", "DCAxy #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, DCAxyAxis}}); + QAHistos.add("hDCAzVsPt", "DCAz #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, DCAzAxis}}); + QAHistos.add("hnSigmaTPCVsPt", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + QAHistos.add("hnSigmaTOFVsPt", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + QAHistos.add("ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}}); + QAHistos.add("etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); + + // processed events + hProcessedEvents->GetXaxis()->SetBinLabel(1, "Events processed"); + hProcessedEvents->GetXaxis()->SetBinLabel(2, "ZORRO"); + hProcessedEvents->GetXaxis()->SetBinLabel(3, "sel8"); + hProcessedEvents->GetXaxis()->SetBinLabel(4, "z-vertex"); + } + void initCCDB(o2::aod::BCsWithTimestamps::iterator const& bc) // inspired by PWGLF/TableProducer/lambdakzerobuilder.cxx + { + if (mRunNumber == bc.runNumber()) { + return; + } + d_bz = 0.f; + + if (applySkimming) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), cfgSkimming.value); + zorro.populateHistRegistry(registry, bc.runNumber()); + } + } + using CollisionCandidates = o2::soa::Join; + // Tables for MC processing + using MCTrackCandidates = o2::soa::Join; + using MCCollisionCandidates = o2::soa::Join; + // Tables for Data processing + using TrackCandidates = o2::soa::Join; + + // Single-Track Selection + template + bool passedSingleTrackSelection(const T1& track) + { + // Single-Track Selections + if (std::abs(track.eta()) > cfgEta) + return false; + if (!track.hasITS()) + return false; + if (!track.hasTPC()) + return false; + if (!track.hasTOF()) + return false; + if (track.tpcNClsFound() < cfgTPCNclsFound) + return false; + if (track.tpcChi2NCl() > cfgTPCChi2Ncl) + return false; + if (track.itsChi2NCl() > cfgITSChi2Ncl) + return false; + if (track.itsNCls() < cfgITScls) + return false; + if (track.pt() > cfgMaxPt) + return false; + if (track.pt() < cfgMinPt) + return false; + if (track.sign() > 0) + return false; + + return true; + } + Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + int mCurrentRun = -1; + void processData(CollisionCandidates const& collisions, + o2::aod::TrackAssoc const& trackIndices, + TrackCandidates const& tracks, + o2::aod::BCsWithTimestamps const&) + { + for (const auto& collision : collisions) // start loop over collisions + { + if (mCurrentRun != collision.bc_as().runNumber()) { // If the run is new then we need to initialize the propagator field + o2::parameters::GRPMagField* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", collision.bc_as().timestamp()); + o2::base::Propagator::initFieldFromGRP(grpo); + mCurrentRun = collision.bc_as().runNumber(); + } + + const auto& bc = collision.bc_as(); + initCCDB(bc); + hProcessedEvents->Fill(0.5); + if (applySkimming) { + if (!zorro.isSelected(bc.globalBC())) { + continue; + } + } + hProcessedEvents->Fill(1.5); + if (sel8 && !collision.sel8()) { + continue; + } + hProcessedEvents->Fill(2.5); + if (std::abs(collision.posZ()) > cutzvertex) { + continue; + } + hProcessedEvents->Fill(3.5); + QAHistos.fill(HIST("hVtxZ"), collision.posZ()); + + const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + + for (const auto& trackId : trackIdsThisCollision) { // start loop over tracks + + const auto& track = tracks.rawIteratorAt(trackId.trackId()); + + std::array dca{track.dcaXY(), track.dcaZ()}; + + if (track.collisionId() != collision.globalIndex()) { + auto trackPar = getTrackParCov(track); + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackPar, 2.f, noMatCorr, &dca); + } + + if (!passedSingleTrackSelection(track)) { + continue; + } + + const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; + const bool isTOFDe_min = std::abs(track.tofNSigmaDe()) > cfgTOFNsigma_min; + const bool isTOFDe_max = std::abs(track.tofNSigmaDe()) < cfgTOFNsigma_max; + + if (track.pt() < ptThresholdPid) { + if (isTPCDe && isTOFDe_max) { + QAHistos.fill(HIST("ptAntiDeuteron"), track.pt()); + QAHistos.fill(HIST("etaAntideuteron"), track.eta()); + QAHistos.fill(HIST("hDCAxyVsPt"), track.pt(), dca[0]); + QAHistos.fill(HIST("hDCAzVsPt"), track.pt(), dca[1]); + QAHistos.fill(HIST("hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); + QAHistos.fill(HIST("hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); + } + } else { + if (isTPCDe && isTOFDe_min && isTOFDe_max) { + QAHistos.fill(HIST("ptAntiDeuteron"), track.pt()); + QAHistos.fill(HIST("etaAntideuteron"), track.eta()); + QAHistos.fill(HIST("hDCAxyVsPt"), track.pt(), dca[0]); + QAHistos.fill(HIST("hDCAzVsPt"), track.pt(), dca[1]); + QAHistos.fill(HIST("hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); + QAHistos.fill(HIST("hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); + } + } + } + } + } + PROCESS_SWITCH(H2fromLb, processData, "processData", false); + + void processMC(MCCollisionCandidates::iterator const&, MCTrackCandidates const& tracks, o2::aod::McParticles const&) + { + for (const auto& track : tracks) { + if (!passedSingleTrackSelection(track)) { + continue; + } + + if (!track.has_mcParticle()) { + continue; + } + + auto mcParticle = track.mcParticle(); + if (mcParticle.pdgCode() == pdgCodeDaughter) { + if (std::abs(mcParticle.y()) > 0.5) { + continue; + } + if (mcParticle.isPhysicalPrimary()) { + bool isFromLb = false; + if (separateAntideuterons) { + for (auto& mom : mcParticle.mothers_as()) { + if (mom.pdgCode() == pdgCodeMother) { // Lambda_b + isFromLb = true; + + break; + } + } + } + if (isFromLb) { + QAHistos.fill(HIST("hDCAxy-FromLb"), track.dcaXY()); + QAHistos.fill(HIST("ptAntiDeuteronFromLb"), track.pt()); + } else { + QAHistos.fill(HIST("hDCAxy-Primary"), track.dcaXY()); + QAHistos.fill(HIST("ptAntiDeuteronPrimary"), track.pt()); + } + } + } + } + } + PROCESS_SWITCH(H2fromLb, processMC, "processMC", true); + + void processGen(o2::aod::McCollision const&, o2::aod::McParticles const& mcParticles) + { + hProcessedEvents->Fill(0.5); + for (const auto& mcParticle : mcParticles) { + if (mcParticle.pdgCode() == pdgCodeMother) { + if (std::abs(mcParticle.y()) <= 0.5) { + QAHistos.fill(HIST("ptGeneratedLb"), mcParticle.pt()); // rinominato + } + } + + if (mcParticle.pdgCode() == pdgCodeDaughter) { + if (std::abs(mcParticle.y()) > 0.5) + continue; + + bool isFromLb = false; + if (mcParticle.has_mothers()) { + for (auto& mom : mcParticle.mothers_as()) { + if (mom.pdgCode() == pdgCodeMother) { + isFromLb = true; + break; + } + } + } + + if (isFromLb) { + QAHistos.fill(HIST("ptAntiDeuteronFromLb"), mcParticle.pt()); + } else if (mcParticle.isPhysicalPrimary()) { + QAHistos.fill(HIST("ptAntiDeuteronPrimary"), mcParticle.pt()); + } + } + } + } + PROCESS_SWITCH(H2fromLb, processGen, "processGen", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"task-h2-from-lb"})}; +} From 9cdabd138146be1778909214d5dd5bae6612c4a1 Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 11:02:46 +0200 Subject: [PATCH 02/10] Fix workflow name capitalization and trailing spaces --- PWGHF/D2H/Tasks/CMakeLists.txt | 5 +---- PWGHF/D2H/Tasks/taskH2fromLb.cxx | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index cd9cd149dc8..110106a9fa0 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -169,10 +169,7 @@ o2physics_add_dpl_workflow(task-hidden-charm PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(task-h2-from-lb +o2physics_add_dpl_workflow(task-h2-from-Lb SOURCES taskH2fromLb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) - - - diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2fromLb.cxx index fe801b8c4cc..3f036feb5c8 100644 --- a/PWGHF/D2H/Tasks/taskH2fromLb.cxx +++ b/PWGHF/D2H/Tasks/taskH2fromLb.cxx @@ -327,5 +327,5 @@ struct H2fromLb { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"task-h2-from-lb"})}; + adaptAnalysisTask(cfgc, TaskName{"task-h2-from-Lb"})}; } From 68d918ea1fb866d7d1584d0d78191d16dedf4a7b Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 11:31:49 +0200 Subject: [PATCH 03/10] Required fixes --- PWGHF/D2H/Tasks/CMakeLists.txt | 2 +- .../Tasks/{taskH2fromLb.cxx => taskH2FromLb.cxx} | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) rename PWGHF/D2H/Tasks/{taskH2fromLb.cxx => taskH2FromLb.cxx} (96%) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 110106a9fa0..256a3ab8498 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -170,6 +170,6 @@ o2physics_add_dpl_workflow(task-hidden-charm COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-h2-from-Lb - SOURCES taskH2fromLb.cxx + SOURCES taskH2FromLb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2FromLb.cxx similarity index 96% rename from PWGHF/D2H/Tasks/taskH2fromLb.cxx rename to PWGHF/D2H/Tasks/taskH2FromLb.cxx index 3f036feb5c8..88baa7cafb1 100644 --- a/PWGHF/D2H/Tasks/taskH2fromLb.cxx +++ b/PWGHF/D2H/Tasks/taskH2FromLb.cxx @@ -75,6 +75,7 @@ struct H2fromLb { Configurable cfgTOFNsigma_min{"cfgTOFNsigma_min", 3.0f, "TOF n sigma min for deuteron PID"}; Configurable cfgTOFNsigma_max{"cfgTOFNsigma_max", 4.0f, "TOF n sigma max for deuteron PID"}; Configurable ptThresholdPid{"ptThresholdPid", 1.0f, "pT threshold to switch between 4 and 3 sigmas for TOF PID"}; + Configurable rapidityCut{"rapidityCut", 0.5f, "Rapidity cut"}; // PDG codes Configurable pdgCodeMother{"pdgCodeMother", -5122, "PDG code of the mother particle (default: anti-Lambda_b)"}; Configurable pdgCodeDaughter{"pdgCodeDaughter", -1000010020, "PDG code of the daughter particle (default: anti-deuteron)"}; @@ -262,13 +263,13 @@ struct H2fromLb { auto mcParticle = track.mcParticle(); if (mcParticle.pdgCode() == pdgCodeDaughter) { - if (std::abs(mcParticle.y()) > 0.5) { + if (std::abs(mcParticle.y()) > rapidityCut) { continue; } if (mcParticle.isPhysicalPrimary()) { bool isFromLb = false; if (separateAntideuterons) { - for (auto& mom : mcParticle.mothers_as()) { + for (const auto& mom : mcParticle.mothers_as()) { if (mom.pdgCode() == pdgCodeMother) { // Lambda_b isFromLb = true; @@ -294,18 +295,18 @@ struct H2fromLb { hProcessedEvents->Fill(0.5); for (const auto& mcParticle : mcParticles) { if (mcParticle.pdgCode() == pdgCodeMother) { - if (std::abs(mcParticle.y()) <= 0.5) { + if (std::abs(mcParticle.y()) <= rapidityCut) { QAHistos.fill(HIST("ptGeneratedLb"), mcParticle.pt()); // rinominato } } if (mcParticle.pdgCode() == pdgCodeDaughter) { - if (std::abs(mcParticle.y()) > 0.5) + if (std::abs(mcParticle.y()) > rapidityCut) continue; bool isFromLb = false; if (mcParticle.has_mothers()) { - for (auto& mom : mcParticle.mothers_as()) { + for (const auto& mom : mcParticle.mothers_as()) { if (mom.pdgCode() == pdgCodeMother) { isFromLb = true; break; @@ -327,5 +328,5 @@ struct H2fromLb { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"task-h2-from-Lb"})}; + adaptAnalysisTask(cfgc)}; } From 13ca4ff7a429d6abc6f9010c2dbfe48bf5b233fc Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 11:47:52 +0200 Subject: [PATCH 04/10] Fix linter: reorder struct members, rename struct to HfTaskH2fromLb --- .../{taskH2FromLb.cxx => taskH2fromLb.cxx} | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) rename PWGHF/D2H/Tasks/{taskH2FromLb.cxx => taskH2fromLb.cxx} (89%) diff --git a/PWGHF/D2H/Tasks/taskH2FromLb.cxx b/PWGHF/D2H/Tasks/taskH2fromLb.cxx similarity index 89% rename from PWGHF/D2H/Tasks/taskH2FromLb.cxx rename to PWGHF/D2H/Tasks/taskH2fromLb.cxx index 88baa7cafb1..5d1b2d90f3a 100644 --- a/PWGHF/D2H/Tasks/taskH2FromLb.cxx +++ b/PWGHF/D2H/Tasks/taskH2fromLb.cxx @@ -9,10 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -/// \brief A filter task for non prompt deuterons +/// \brief A filter task for non prompt deuterons from beauty-hadron decays /// \author Marta Razza marta.razza@cern.ch /// \author Francesca Ercolessi francesca.ercolessi@cern.ch -/// \since Dec 17, 2025 +/// \since May 25, 2026 #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" @@ -48,17 +48,11 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -struct H2fromLb { +struct HfTaskH2fromLb { Zorro zorro; - OutputObj zorroSummary{"zorroSummary"}; - o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - // Define a histograms and registries - HistogramRegistry QAHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - OutputObj hProcessedEvents{TH1D("hProcessedEvents", "Event filtered;; Number of events", 4, 0., 4.)}; - Configurable cutzvertex{"cutzvertex", 10.0f, "Accepted z-vertex range (cm)"}; Configurable applySkimming{"applySkimming", false, "Skimmed dataset processing"}; Configurable cfgSkimming{"cfgSkimming", "fH2fromLb", "Configurable for skimming"}; @@ -72,8 +66,8 @@ struct H2fromLb { Configurable cfgMaxPt{"cfgMaxPt", 5.0f, "Maximum pT cut"}; Configurable cfgMinPt{"cfgMinPt", 0.5f, "Minimum pT cut"}; Configurable cfgTPCNsigma{"cfgTPCNsigma", 4.0f, "TPC n sigma for deuteron PID"}; - Configurable cfgTOFNsigma_min{"cfgTOFNsigma_min", 3.0f, "TOF n sigma min for deuteron PID"}; - Configurable cfgTOFNsigma_max{"cfgTOFNsigma_max", 4.0f, "TOF n sigma max for deuteron PID"}; + Configurable cfgTofNsigmaMin{"cfgTofNsigmaMin", 3.0f, "TOF n sigma min for deuteron PID"}; + Configurable cfgTofNsigmaMax{"cfgTofNsigmaMax", 4.0f, "TOF n sigma max for deuteron PID"}; Configurable ptThresholdPid{"ptThresholdPid", 1.0f, "pT threshold to switch between 4 and 3 sigmas for TOF PID"}; Configurable rapidityCut{"rapidityCut", 0.5f, "Rapidity cut"}; // PDG codes @@ -82,13 +76,21 @@ struct H2fromLb { int mRunNumber = 0; float d_bz = 0.f; - HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + int mCurrentRun = -1; framework::Service ccdb; + + Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + + HistogramRegistry QAHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + OutputObj zorroSummary{"zorroSummary"}; + OutputObj hProcessedEvents{TH1D("hProcessedEvents", "Event filtered;; Number of events", 4, 0., 4.)}; + void init(framework::InitContext&) { - - ccdb->setURL("http://alice-ccdb.cern.ch"); // Set CCDB URL to get magnetic field + ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); @@ -99,8 +101,8 @@ struct H2fromLb { ConfigurableAxis ptAxis{"ptAxis", {100, 0., 10.f}, "p_{T} GeV/c"}; ConfigurableAxis nSigmaAxis{"nSigmaAxis", {200, -10.f, 10.f}, "nSigma"}; - ConfigurableAxis DCAxyAxis{"DCAxyAxis", {1000, -0.2f, 0.2f}, "DCA xy (cm)"}; - ConfigurableAxis DCAzAxis{"DCAzAxis", {1000, -0.2f, 0.2f}, "DCA z (cm)"}; + ConfigurableAxis dcaXyAxis{"dcaXyAxis", {1000, -0.2f, 0.2f}, "DCA xy (cm)"}; + ConfigurableAxis dcaZAxis{"dcaZAxis", {1000, -0.2f, 0.2f}, "DCA z (cm)"}; // general QA histograms QAHistos.add("hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); @@ -109,8 +111,8 @@ struct H2fromLb { QAHistos.add("ptAntiDeuteronFromLb", "ptAntiDeuteronFromLbReco", HistType::kTH1F, {ptAxis}); QAHistos.add("hDCAxy-Primary", "DCAxy-Primary", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); QAHistos.add("hDCAxy-FromLb", "DCAxy-FromLb", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); - QAHistos.add("hDCAxyVsPt", "DCAxy #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, DCAxyAxis}}); - QAHistos.add("hDCAzVsPt", "DCAz #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, DCAzAxis}}); + QAHistos.add("hDCAxyVsPt", "DCAxy #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + QAHistos.add("hDCAzVsPt", "DCAz #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaZAxis}}); QAHistos.add("hnSigmaTPCVsPt", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); QAHistos.add("hnSigmaTOFVsPt", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); QAHistos.add("ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}}); @@ -122,7 +124,8 @@ struct H2fromLb { hProcessedEvents->GetXaxis()->SetBinLabel(3, "sel8"); hProcessedEvents->GetXaxis()->SetBinLabel(4, "z-vertex"); } - void initCCDB(o2::aod::BCsWithTimestamps::iterator const& bc) // inspired by PWGLF/TableProducer/lambdakzerobuilder.cxx + + void initCCDB(o2::aod::BCsWithTimestamps::iterator const& bc) { if (mRunNumber == bc.runNumber()) { return; @@ -134,18 +137,15 @@ struct H2fromLb { zorro.populateHistRegistry(registry, bc.runNumber()); } } + using CollisionCandidates = o2::soa::Join; - // Tables for MC processing using MCTrackCandidates = o2::soa::Join; using MCCollisionCandidates = o2::soa::Join; - // Tables for Data processing using TrackCandidates = o2::soa::Join; - // Single-Track Selection template bool passedSingleTrackSelection(const T1& track) { - // Single-Track Selections if (std::abs(track.eta()) > cfgEta) return false; if (!track.hasITS()) @@ -171,16 +171,14 @@ struct H2fromLb { return true; } - Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; - int mCurrentRun = -1; + void processData(CollisionCandidates const& collisions, o2::aod::TrackAssoc const& trackIndices, TrackCandidates const& tracks, o2::aod::BCsWithTimestamps const&) { - for (const auto& collision : collisions) // start loop over collisions - { - if (mCurrentRun != collision.bc_as().runNumber()) { // If the run is new then we need to initialize the propagator field + for (const auto& collision : collisions) { + if (mCurrentRun != collision.bc_as().runNumber()) { o2::parameters::GRPMagField* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", collision.bc_as().timestamp()); o2::base::Propagator::initFieldFromGRP(grpo); mCurrentRun = collision.bc_as().runNumber(); @@ -207,10 +205,8 @@ struct H2fromLb { const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - for (const auto& trackId : trackIdsThisCollision) { // start loop over tracks - + for (const auto& trackId : trackIdsThisCollision) { const auto& track = tracks.rawIteratorAt(trackId.trackId()); - std::array dca{track.dcaXY(), track.dcaZ()}; if (track.collisionId() != collision.globalIndex()) { @@ -223,8 +219,8 @@ struct H2fromLb { } const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; - const bool isTOFDe_min = std::abs(track.tofNSigmaDe()) > cfgTOFNsigma_min; - const bool isTOFDe_max = std::abs(track.tofNSigmaDe()) < cfgTOFNsigma_max; + const bool isTOFDe_min = std::abs(track.tofNSigmaDe()) > cfgTofNsigmaMin; + const bool isTOFDe_max = std::abs(track.tofNSigmaDe()) < cfgTofNsigmaMax; if (track.pt() < ptThresholdPid) { if (isTPCDe && isTOFDe_max) { @@ -248,7 +244,7 @@ struct H2fromLb { } } } - PROCESS_SWITCH(H2fromLb, processData, "processData", false); + PROCESS_SWITCH(HfTaskH2fromLb, processData, "processData", false); void processMC(MCCollisionCandidates::iterator const&, MCTrackCandidates const& tracks, o2::aod::McParticles const&) { @@ -270,9 +266,8 @@ struct H2fromLb { bool isFromLb = false; if (separateAntideuterons) { for (const auto& mom : mcParticle.mothers_as()) { - if (mom.pdgCode() == pdgCodeMother) { // Lambda_b + if (mom.pdgCode() == pdgCodeMother) { isFromLb = true; - break; } } @@ -288,7 +283,7 @@ struct H2fromLb { } } } - PROCESS_SWITCH(H2fromLb, processMC, "processMC", true); + PROCESS_SWITCH(HfTaskH2fromLb, processMC, "processMC", true); void processGen(o2::aod::McCollision const&, o2::aod::McParticles const& mcParticles) { @@ -296,7 +291,7 @@ struct H2fromLb { for (const auto& mcParticle : mcParticles) { if (mcParticle.pdgCode() == pdgCodeMother) { if (std::abs(mcParticle.y()) <= rapidityCut) { - QAHistos.fill(HIST("ptGeneratedLb"), mcParticle.pt()); // rinominato + QAHistos.fill(HIST("ptGeneratedLb"), mcParticle.pt()); } } @@ -322,11 +317,11 @@ struct H2fromLb { } } } - PROCESS_SWITCH(H2fromLb, processGen, "processGen", false); + PROCESS_SWITCH(HfTaskH2fromLb, processGen, "processGen", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } From 94726f6dad2581ca8a0844b6eca2b68103c3c40d Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 11:55:45 +0200 Subject: [PATCH 05/10] Remove duplicate using declarations --- PWGHF/D2H/Tasks/taskH2fromLb.cxx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2fromLb.cxx index 5d1b2d90f3a..bbdab28f5a4 100644 --- a/PWGHF/D2H/Tasks/taskH2fromLb.cxx +++ b/PWGHF/D2H/Tasks/taskH2fromLb.cxx @@ -80,6 +80,11 @@ struct HfTaskH2fromLb { framework::Service ccdb; + using CollisionCandidates = o2::soa::Join; + using MCTrackCandidates = o2::soa::Join; + using MCCollisionCandidates = o2::soa::Join; + using TrackCandidates = o2::soa::Join; + Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; HistogramRegistry QAHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; @@ -104,7 +109,6 @@ struct HfTaskH2fromLb { ConfigurableAxis dcaXyAxis{"dcaXyAxis", {1000, -0.2f, 0.2f}, "DCA xy (cm)"}; ConfigurableAxis dcaZAxis{"dcaZAxis", {1000, -0.2f, 0.2f}, "DCA z (cm)"}; - // general QA histograms QAHistos.add("hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); QAHistos.add("ptGeneratedLb", "ptGeneratedLb", HistType::kTH1F, {ptAxis}); QAHistos.add("ptAntiDeuteronPrimary", "ptAntiDeuteronPrimaryReco", HistType::kTH1F, {ptAxis}); @@ -118,7 +122,6 @@ struct HfTaskH2fromLb { QAHistos.add("ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}}); QAHistos.add("etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); - // processed events hProcessedEvents->GetXaxis()->SetBinLabel(1, "Events processed"); hProcessedEvents->GetXaxis()->SetBinLabel(2, "ZORRO"); hProcessedEvents->GetXaxis()->SetBinLabel(3, "sel8"); @@ -138,11 +141,6 @@ struct HfTaskH2fromLb { } } - using CollisionCandidates = o2::soa::Join; - using MCTrackCandidates = o2::soa::Join; - using MCCollisionCandidates = o2::soa::Join; - using TrackCandidates = o2::soa::Join; - template bool passedSingleTrackSelection(const T1& track) { @@ -324,4 +322,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ adaptAnalysisTask(cfgc)}; -} +} \ No newline at end of file From 2c98e9b4833a0acaf76e667928c555eb4ebb6e71 Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 11:58:10 +0200 Subject: [PATCH 06/10] clang-formart --- PWGHF/D2H/Tasks/taskH2fromLb.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2fromLb.cxx index bbdab28f5a4..971078e72d0 100644 --- a/PWGHF/D2H/Tasks/taskH2fromLb.cxx +++ b/PWGHF/D2H/Tasks/taskH2fromLb.cxx @@ -322,4 +322,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ adaptAnalysisTask(cfgc)}; -} \ No newline at end of file +} From 24bd803bbc107fec8d5563bac94414df9ee88fd4 Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 12:14:57 +0200 Subject: [PATCH 07/10] Fix cpplint: add missing #include --- PWGHF/D2H/Tasks/taskH2fromLb.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2fromLb.cxx index 971078e72d0..2079b4cf4be 100644 --- a/PWGHF/D2H/Tasks/taskH2fromLb.cxx +++ b/PWGHF/D2H/Tasks/taskH2fromLb.cxx @@ -41,6 +41,7 @@ #include #include +#include #include #include From 397cc2cc157f97d7e57ab553a59b1ff7bfde874e Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 12:20:32 +0200 Subject: [PATCH 08/10] format --- PWGHF/D2H/Tasks/taskH2fromLb.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2fromLb.cxx index 2079b4cf4be..7820ffc7698 100644 --- a/PWGHF/D2H/Tasks/taskH2fromLb.cxx +++ b/PWGHF/D2H/Tasks/taskH2fromLb.cxx @@ -41,9 +41,9 @@ #include #include -#include #include #include +#include using namespace o2; using namespace o2::framework; From 7c51eec3141f347597781951b7897b080f6719bd Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Mon, 25 May 2026 12:49:37 +0200 Subject: [PATCH 09/10] Fix CMakeLists: lowercase workflow name --- PWGHF/D2H/Tasks/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 256a3ab8498..47cfe737ef1 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -169,7 +169,7 @@ o2physics_add_dpl_workflow(task-hidden-charm PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(task-h2-from-Lb - SOURCES taskH2FromLb.cxx +o2physics_add_dpl_workflow(task-h2-from-lb + SOURCES taskH2fromLb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) From 33a39a7cbc08817311894de2016333a1b5e9a145 Mon Sep 17 00:00:00 2001 From: Marta Razza Date: Tue, 26 May 2026 13:00:48 +0200 Subject: [PATCH 10/10] Fix: rename task file to match workflow name (taskH2FromLb) --- PWGHF/D2H/Tasks/CMakeLists.txt | 2 +- PWGHF/D2H/Tasks/{taskH2fromLb.cxx => taskH2FromLb.cxx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename PWGHF/D2H/Tasks/{taskH2fromLb.cxx => taskH2FromLb.cxx} (100%) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 47cfe737ef1..f11c48baf93 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -170,6 +170,6 @@ o2physics_add_dpl_workflow(task-hidden-charm COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-h2-from-lb - SOURCES taskH2fromLb.cxx + SOURCES taskH2FromLb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGHF/D2H/Tasks/taskH2fromLb.cxx b/PWGHF/D2H/Tasks/taskH2FromLb.cxx similarity index 100% rename from PWGHF/D2H/Tasks/taskH2fromLb.cxx rename to PWGHF/D2H/Tasks/taskH2FromLb.cxx