From bb62a85ed5b50bdbf55945c07c09638bca78ce48 Mon Sep 17 00:00:00 2001 From: Ran Tu Date: Wed, 6 May 2026 16:51:05 +0200 Subject: [PATCH 1/3] Add Lc UPC task file --- PWGHF/D2H/Tasks/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 1ffbbf3cd33..3bf0716719f 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -1,6 +1,5 @@ -# 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. +# 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". @@ -114,6 +113,11 @@ o2physics_add_dpl_workflow(task-lc PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-upc-lc + SOURCES taskUpcLc.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(task-lc-to-k0s-p SOURCES taskLcToK0sP.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore From 82aaa4ed11a8555de38b6a0e0a147fbf455c236e Mon Sep 17 00:00:00 2001 From: Ran Tu Date: Wed, 6 May 2026 16:57:32 +0200 Subject: [PATCH 2/3] Add UPC Lc task --- PWGHF/D2H/Tasks/taskUpcLc.cxx | 360 ++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 PWGHF/D2H/Tasks/taskUpcLc.cxx diff --git a/PWGHF/D2H/Tasks/taskUpcLc.cxx b/PWGHF/D2H/Tasks/taskUpcLc.cxx new file mode 100644 index 00000000000..6008073c7b3 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskUpcLc.cxx @@ -0,0 +1,360 @@ +// 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. + +/// \file taskUpcLc.cxx +/// \brief Λc± → p± K∓ π± analysis task +/// \note Extended from taskLc +/// +/// \author Biao Zhang , Heidelberg University +/// \author Ran Tu , Fudan University + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGHF/Utils/utilsEvSelHf.h" +#include "PWGHF/Utils/utilsUpcHf.h" +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UPCHelpers.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include // std::vector + +using namespace o2; +using namespace o2::analysis; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_evsel; +using namespace o2::analysis::hf_upc; + +namespace o2::aod +{ +namespace full +{ +DECLARE_SOA_COLUMN(M, m, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(BkgScore, bkgScore, float); +DECLARE_SOA_COLUMN(PromptScore, promptScore, float); +DECLARE_SOA_COLUMN(FdScore, fdScore, float); +DECLARE_SOA_COLUMN(PtProng0, ptProng0, float); +DECLARE_SOA_COLUMN(PtProng1, ptProng1, float); +DECLARE_SOA_COLUMN(PtProng2, ptProng2, float); +DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); +DECLARE_SOA_COLUMN(DecayLength, decayLength, float); +DECLARE_SOA_COLUMN(Cpa, cpa, float); +DECLARE_SOA_COLUMN(PvContributors, pvContributors, float); +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); +DECLARE_SOA_COLUMN(Vtz, vtz, float); +DECLARE_SOA_COLUMN(AmpFV0A, ampFV0A, float); +DECLARE_SOA_COLUMN(AmpFT0A, ampFT0A, float); +DECLARE_SOA_COLUMN(AmpFT0C, ampFT0C, float); +DECLARE_SOA_COLUMN(ZdcTimeZNA, zdcTimeZNA, float); +DECLARE_SOA_COLUMN(ZdcTimeZNC, zdcTimeZNC, float); +} // namespace full +DECLARE_SOA_TABLE(HfUpcQa, "AOD", "HFUPCQA", + full::PvContributors, + full::Multiplicity, + full::Vtz, + full::AmpFV0A, + full::AmpFT0A, + full::AmpFT0C, + full::ZdcTimeZNA, + full::ZdcTimeZNC); + +DECLARE_SOA_TABLE(HfUpcLcBdtInfos, "AOD", "HFUPCLCBDTINFOS", + full::M, + full::Pt, + full::BkgScore, + full::PromptScore, + full::FdScore, + full::AmpFV0A, + full::AmpFT0A, + full::AmpFT0C, + full::ZdcTimeZNA, + full::ZdcTimeZNC); + +DECLARE_SOA_TABLE(HfUpcLcInfos, "AOD", "HFUPCLCINFOS", + full::M, + full::Pt, + full::PtProng0, + full::PtProng1, + full::PtProng2, + full::Chi2PCA, + full::DecayLength, + full::Cpa, + full::AmpFV0A, + full::AmpFT0A, + full::AmpFT0C, + full::ZdcTimeZNA, + full::ZdcTimeZNC); +} // namespace o2::aod + +/// Λc± → p± K∓ π± analysis task +struct HfTaskUpcLc { + Produces rowCandUpcBdt; + Produces rowCandUpc; + Produces rowUpcQa; + + Configurable selectionFlagLc{"selectionFlagLc", 1, "Selection Flag for Lc"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable> binsPt{"binsPt", std::vector{hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits"}; + Configurable fillTreeOnlySingleGap{"fillTreeOnlySingleGap", false, "Only fill the tree for candidates that pass the single-gap UPC events"}; + Configurable fillTreeUpcQa{"fillTreeUpcQa", false, "Fill Tree for UPC QA"}; + Configurable verticesWithUpc{"verticesWithUpc", false, "Consider vertices with UPC settings"}; + // CCDB configuration + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + + HfEventSelection hfEvSel; // event selection and monitoring + HfUpcGapThresholds upcThresholds; // UPC gap determination thresholds + SliceCache cache; + Service ccdb{}; + + using Collisions = soa::Join; + + using LcCandidates = soa::Filtered>; + using LcCandidatesMl = soa::Filtered>; + + Filter filterSelectCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlagLc || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlagLc; + Preslice candLcPerCollision = aod::hf_cand::collisionId; + PresliceUnsorted colPerMcCollision = aod::mcparticle::mcCollisionId; + + HistogramRegistry registry{"registry", {}}; + + // Factors for conversion between units + constexpr static float CtToProperLifetimePs = 1.f / o2::constants::physics::LightSpeedCm2PS; + constexpr static float NanoToPico = 1000.f; + // Names of folders and suffixes for MC signal histograms + constexpr static std::string_view SignalFolders[] = {"signal", "prompt", "nonprompt"}; + constexpr static std::string_view SignalSuffixes[] = {"", "Prompt", "NonPrompt"}; + + enum MlClasses : int { + MlClassBackground = 0, + MlClassPrompt, + MlClassNonPrompt, + NumberOfMlClasses + }; + + void init(InitContext&) + { + const std::array doprocess{doprocessDataWithMlWithUpc, doprocessDataStdWithUpc}; + if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { + LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); + } + + auto vbins = (std::vector)binsPt; + registry.add("Data/fitInfo/ampFT0A_vs_ampFT0C", "FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)", {HistType::kTH2F, {{500, 0., 500}, {500, 0., 500}}}); + registry.add("Data/zdc/energyZNA_vs_energyZNC", "ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)", {HistType::kTH2F, {{100, 0., 10}, {100, 0., 10}}}); + registry.add("Data/zdc/timeZNA_vs_timeZNC", "ZNA vs ZNC time;ZNA Time;ZNC time", {HistType::kTH2F, {{200, -10., 10}, {200, -10., 10}}}); + registry.add("Data/hUpcGapAfterSelection", "UPC gap type after selection;Gap side;Counts", {HistType::kTH1F, {{7, -1.5, 5.5}}}); + registry.add("Data/hUpcMulti", "Multiplicity of UPC events;Multiplicity;Counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); + registry.add("Data/hUpcVtz", "Vertex Z position of UPC events;Vz (cm);Counts", {HistType::kTH1F, {{200, -10., 10.}}}); + + hfEvSel.addHistograms(registry); + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } + + /// Evaluate centrality/multiplicity percentile (centrality estimator is automatically selected based on the used table) + /// \param collision is collision + /// \return centrality/multiplicity percentile of the collision + template + float evaluateCentralityColl(const Coll& collision) + { + return o2::hf_centrality::getCentralityColl(collision); + } + + template + void runAnalysisPerCollisionDataWithUpc(CollType const& collisions, + CandType const& candidates, + BCsType const& bcs, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds + + ) + { + for (const auto& collision : collisions) { + float centrality{-1.f}; + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc(collision, centrality, ccdb, registry, bcs); + if (rejectionMask != 0) { + /// at least one event selection not satisfied --> reject the candidate + continue; + } + const auto thisCollId = collision.globalIndex(); + const auto& groupedLcCandidates = candidates.sliceBy(candLcPerCollision, thisCollId); + const auto numPvContributors = collision.numContrib(); + const auto& bc = collision.template bc_as(); + + // Determine gap type using SGSelector with BC range checking + const auto gapResult = hf_upc::determineGapType(collision, bcs, upcThresholds); + const int gap = gapResult.value; + const int upc_flag = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) ? 1 : 0; + + // Use the BC with FIT activity if available from SGSelector + auto bcForUPC = bc; + if (gapResult.bc) { + bcForUPC = *(gapResult.bc); + } + + // Get FIT information from the UPC BC + upchelpers::FITInfo fitInfo{}; + udhelpers::getFITinfo(fitInfo, bcForUPC, bcs, ft0s, fv0as, fdds); + + // Get ZDC energies if available (extract once and reuse) + const bool hasZdc = bcForUPC.has_zdc(); + float zdcEnergyZNA = -1.f; + float zdcEnergyZNC = -1.f; + float zdcTimeZNA = -1.f; + float zdcTimeZNC = -1.f; + if (verticesWithUpc && !upc_flag) { + continue; + } + if (hasZdc) { + const auto zdc = bcForUPC.zdc(); + zdcEnergyZNA = zdc.energyCommonZNA(); + zdcEnergyZNC = zdc.energyCommonZNC(); + zdcTimeZNA = zdc.timeZNA(); + zdcTimeZNC = zdc.timeZNC(); + registry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C); + registry.fill(HIST("Data/zdc/energyZNA_vs_energyZNC"), zdcEnergyZNA, zdcEnergyZNC); + registry.fill(HIST("Data/zdc/timeZNA_vs_timeZNC"), zdcTimeZNA, zdcTimeZNC); + registry.fill(HIST("Data/hUpcGapAfterSelection"), static_cast(gap)); + } + if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { + registry.fill(HIST("Data/hUpcMulti"), collision.multNTracksPV()); + registry.fill(HIST("Data/hUpcVtz"), collision.posZ()); + } + if (fillTreeUpcQa) { + rowUpcQa(static_cast(numPvContributors), collision.multNTracksPV(), collision.posZ(), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)); + } + + for (const auto& candidate : groupedLcCandidates) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { + continue; + } + const auto pt = candidate.pt(); + const auto ptProng0 = candidate.ptProng0(); + const auto ptProng1 = candidate.ptProng1(); + const auto ptProng2 = candidate.ptProng2(); + const auto decayLength = candidate.decayLength(); + const auto chi2PCA = candidate.chi2PCA(); + const auto cpa = candidate.cpa(); + + double outputBkg(-1), outputPrompt(-1), outputFD(-1); + + auto fillTHnData = [&](bool isPKPi) { + const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + + if constexpr (FillMl) { + const auto& mlProb = isPKPi ? candidate.mlProbLcToPKPi() : candidate.mlProbLcToPiKP(); + if (mlProb.size() == NumberOfMlClasses) { + outputBkg = mlProb[MlClassBackground]; /// bkg score + outputPrompt = mlProb[MlClassPrompt]; /// prompt score + outputFD = mlProb[MlClassNonPrompt]; /// non-prompt score + } + /// Fill the ML outputScores and variables of candidate + if (fillTreeOnlySingleGap) { + if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { + rowCandUpcBdt(massLc, pt, outputBkg, outputPrompt, outputFD, static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)); + } + } else { + rowCandUpcBdt(massLc, pt, outputBkg, outputPrompt, outputFD, static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)); + } + + } else { + if (fillTreeOnlySingleGap) { + if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { + rowCandUpc(massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)); + } + } else { + rowCandUpc(massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)); + } + } + }; + + if (candidate.isSelLcToPKPi() >= selectionFlagLc) { + fillTHnData(true); + } + if (candidate.isSelLcToPiKP() >= selectionFlagLc) { + fillTHnData(false); + } + } + } + } + + void processDataWithMlWithUpc(soa::Join const& collisions, + aod::BcFullInfos const& bcs, + LcCandidatesMl const& selectedLcCandidatesMl, + aod::Tracks const&, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds, + aod::Zdcs const& /*zdcs*/) + { + runAnalysisPerCollisionDataWithUpc(collisions, selectedLcCandidatesMl, bcs, ft0s, fv0as, fdds); + } + PROCESS_SWITCH(HfTaskUpcLc, processDataWithMlWithUpc, "Process real data with the ML method with UPC", false); + + void processDataStdWithUpc(soa::Join const& collisions, + aod::BcFullInfos const& bcs, + LcCandidates const& selectedLcCandidates, + aod::Tracks const&, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds, + aod::Zdcs const& /*zdcs*/) + { + runAnalysisPerCollisionDataWithUpc(collisions, selectedLcCandidates, bcs, ft0s, fv0as, fdds); + } + PROCESS_SWITCH(HfTaskUpcLc, processDataStdWithUpc, "Process real data with the standard method with UPC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 25319a592a3deabdb5e9c024e8658a67e665fc61 Mon Sep 17 00:00:00 2001 From: Ran Tu Date: Wed, 6 May 2026 17:28:37 +0200 Subject: [PATCH 3/3] remove upc part --- PWGHF/D2H/Tasks/CMakeLists.txt | 5 +- PWGHF/D2H/Tasks/taskLc.cxx | 212 ++------------------------------- 2 files changed, 13 insertions(+), 204 deletions(-) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 3bf0716719f..96296bf2c6b 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -1,5 +1,6 @@ -# 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. +# 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". diff --git a/PWGHF/D2H/Tasks/taskLc.cxx b/PWGHF/D2H/Tasks/taskLc.cxx index e7144cf505d..805ae3f05ed 100644 --- a/PWGHF/D2H/Tasks/taskLc.cxx +++ b/PWGHF/D2H/Tasks/taskLc.cxx @@ -17,7 +17,6 @@ /// \author Vít Kučera , CERN /// \author Annalena Kalteyer , GSI Darmstadt /// \author Biao Zhang , Heidelberg University -/// \author Ran Tu , Fudan University /// \author Oleksii Lubynets , Heidelberg University, GSI Darmstadt #include "PWGHF/Core/CentralityEstimation.h" @@ -29,9 +28,6 @@ #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/Utils/utilsEvSelHf.h" -#include "PWGHF/Utils/utilsUpcHf.h" -#include "PWGUD/Core/SGSelector.h" -#include "PWGUD/Core/UPCHelpers.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" @@ -69,7 +65,6 @@ using namespace o2::framework::expressions; using namespace o2::hf_centrality; using namespace o2::hf_occupancy; using namespace o2::hf_evsel; -using namespace o2::analysis::hf_upc; /// Λc± → p± K∓ π± analysis task struct HfTaskLc { @@ -79,7 +74,6 @@ struct HfTaskLc { Configurable> binsPt{"binsPt", std::vector{hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits"}; // ThnSparse for ML outputScores and Vars Configurable fillTHn{"fillTHn", false, "fill THn"}; - Configurable fillUPCTHnLite{"fillUPCTHnLite", false, "fill THn"}; Configurable storeOccupancy{"storeOccupancy", true, "Flag to store occupancy information"}; Configurable occEstimator{"occEstimator", 2, "Occupancy estimation (None: 0, ITS: 1, FT0C: 2)"}; Configurable storeProperLifetime{"storeProperLifetime", false, "Flag to store proper lifetime"}; @@ -88,8 +82,7 @@ struct HfTaskLc { Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; - HfEventSelection hfEvSel; // event selection and monitoring - HfUpcGapThresholds upcThresholds; // UPC gap determination thresholds + HfEventSelection hfEvSel; // event selection and monitoring SliceCache cache; Service ccdb{}; @@ -127,13 +120,7 @@ struct HfTaskLc { ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Number of PV contributors"}; ConfigurableAxis thnConfigAxisOccupancy{"thnConfigAxisOccupancy", {14, 0, 14000}, "axis for centrality"}; ConfigurableAxis thnConfigAxisProperLifetime{"thnConfigAxisProperLifetime", {200, 0, 2}, "Proper lifetime, ps"}; - ConfigurableAxis thnConfigAxisGapType{"thnConfigAxisGapType", {7, -1.5, 5.5}, "axis for UPC gap type (see TrueGap enum in o2::aod::sgselector)"}; - ConfigurableAxis thnConfigAxisFV0A{"thnConfigAxisFV0A", {1001, -1.5, 999.5}, "axis for FV0-A amplitude (a.u.)"}; - ConfigurableAxis thnConfigAxisFT0{"thnConfigAxisFT0", {1001, -1.5, 999.5}, "axis for FT0 amplitude (a.u.)"}; - ConfigurableAxis thnConfigAxisZN{"thnConfigAxisZN", {510, -1.5, 49.5}, "axis for ZN energy (a.u.)"}; - ConfigurableAxis thnConfigAxisZNTime{"thnConfigAxisZNTime", {200, -10, 10}, "axis for ZN energy (a.u.)"}; HistogramRegistry registry{"registry", {}}; - HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Factors for conversion between units constexpr static float CtToProperLifetimePs = 1.f / o2::constants::physics::LightSpeedCm2PS; @@ -157,13 +144,12 @@ struct HfTaskLc { void init(InitContext&) { - const std::array doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, doprocessDataWithMl, doprocessDataWithMlWithFT0C, doprocessDataWithMlWithFT0M, doprocessDataWithMlWithUpc, doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M, doprocessMcWithMl, doprocessMcWithMlWithFT0C, doprocessMcWithMlWithFT0M, doprocessDataStdWithUpc}; + const std::array doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, doprocessDataWithMl, doprocessDataWithMlWithFT0C, doprocessDataWithMlWithFT0M, doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M, doprocessMcWithMl, doprocessMcWithMlWithFT0C, doprocessMcWithMlWithFT0M}; if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); } - const bool isData = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M || doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M || doprocessDataWithMlWithUpc; - const bool isUpc = doprocessDataWithMlWithUpc || doprocessDataStdWithUpc; + const bool isData = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M || doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M; auto addHistogramsRec = [&](const std::string& histoName, const std::string& xAxisTitle, const std::string& yAxisTitle, const HistogramConfigSpec& configSpec) { if (isData) { @@ -266,14 +252,6 @@ struct HfTaskLc { /// decay length error addHistogramsRec("hDecLenErrVsPt", "decay length error (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); - if (isUpc) { - qaRegistry.add("Data/fitInfo/ampFT0A_vs_ampFT0C", "FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)", {HistType::kTH2F, {{500, 0., 500}, {500, 0., 500}}}); - qaRegistry.add("Data/zdc/energyZNA_vs_energyZNC", "ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)", {HistType::kTH2F, {{100, 0., 10}, {100, 0., 10}}}); - qaRegistry.add("Data/zdc/timeZNA_vs_timeZNC", "ZNA vs ZNC time;ZNA Time;ZNC time", {HistType::kTH2F, {{200, -10., 10}, {200, -10., 10}}}); - qaRegistry.add("Data/hUpcGapAfterSelection", "UPC gap type after selection;Gap side;Counts", {HistType::kTH1F, {{7, -1.5, 5.5}}}); - qaRegistry.add("Data/hUpcMulti", "Multiplicity of UPC events;Multiplicity;Counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); - qaRegistry.add("Data/hUpcVtz", "Vertex Z position of UPC events;Vz (cm);Counts", {HistType::kTH1F, {{200, -10., 10.}}}); - } if (fillTHn) { const AxisSpec thnAxisMass{thnConfigAxisMass, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T}(#Lambda_{c}^{+}) (GeV/#it{c})"}; @@ -293,39 +271,26 @@ struct HfTaskLc { const AxisSpec thnAxisTracklets{thnConfigAxisNumPvContr, "Number of PV contributors"}; const AxisSpec thnAxisOccupancy{thnConfigAxisOccupancy, "Occupancy"}; const AxisSpec thnAxisProperLifetime{thnConfigAxisProperLifetime, "T_{proper} (ps)"}; - const AxisSpec thnAxisFV0A{thnConfigAxisFV0A, "FV0-A amplitude"}; - const AxisSpec thnAxisFT0A{thnConfigAxisFT0, "FT0-A amplitude"}; - const AxisSpec thnAxisFT0C{thnConfigAxisFT0, "FT0-C amplitude"}; - const AxisSpec thnAxisZNA{thnConfigAxisZN, "ZNA energy"}; - const AxisSpec thnAxisZNC{thnConfigAxisZN, "ZNC energy"}; - const AxisSpec thnAxisZNATime{thnConfigAxisZNTime, "ZNA time"}; - const AxisSpec thnAxisZNCTime{thnConfigAxisZNTime, "ZNC time"}; - - bool const isDataWithMl = doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M || doprocessDataWithMlWithUpc; + + bool const isDataWithMl = doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M; bool const isMcWithMl = doprocessMcWithMl || doprocessMcWithMlWithFT0C || doprocessMcWithMlWithFT0M; - bool const isDataStd = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M || doprocessDataStdWithUpc; + bool const isDataStd = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M; bool const isMcStd = doprocessMcStd || doprocessMcStdWithFT0C || doprocessMcStdWithFT0M; - std::vector axesStd, axesWithBdt, axesGen, axesUpc, axesUpcWithBdt; + std::vector axesStd, axesWithBdt, axesGen; - if (isDataStd && !isUpc) { + if (isDataStd) { axesStd = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets}; } - if (isDataStd && isUpc) { - axesUpc = {thnAxisMass, thnAxisPt, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets, thnAxisFV0A, thnAxisFT0A, thnAxisFT0C, thnAxisZNA, thnAxisZNC, thnAxisZNATime, thnAxisZNCTime}; - } if (isMcStd) { axesStd = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets, thnAxisPtB, thnAxisCanType}; } if (isMcStd || isMcWithMl) { axesGen = {thnAxisPt, thnAxisCentrality, thnAxisY, thnAxisTracklets, thnAxisPtB, thnAxisCanType}; } - if (isDataWithMl && !isUpc) { + if (isDataWithMl) { axesWithBdt = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets}; } - if (isDataWithMl && isUpc) { - axesUpcWithBdt = {thnAxisMass, thnAxisPt, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets, thnAxisFV0A, thnAxisFT0A, thnAxisFT0C, thnAxisZNA, thnAxisZNC, thnAxisZNATime, thnAxisZNCTime}; - } if (isMcWithMl) { axesWithBdt = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets, thnAxisPtB, thnAxisCanType}; } @@ -344,13 +309,7 @@ struct HfTaskLc { } } } - if (isUpc) { - if (isDataStd) { - registry.add("hnLcUpcVars", "THn for Lambdac candidates for Data in UPC", HistType::kTHnSparseF, axesUpc); - } else if (isDataWithMl) { - registry.add("hnLcUpcVarsWithBdt", "THn for Lambdac candidates with BDT scores for data in UPC", HistType::kTHnSparseF, axesUpcWithBdt); - } - } else if (isDataWithMl) { + if (isDataWithMl) { registry.add("hnLcVarsWithBdt", "THn for Lambdac candidates with BDT scores for data with ML", HistType::kTHnSparseF, axesWithBdt); } else if (isMcWithMl) { registry.add("hnLcVarsWithBdt", "THn for Lambdac candidates with BDT scores for mc with ML", HistType::kTHnSparseF, axesWithBdt); @@ -363,10 +322,6 @@ struct HfTaskLc { } } - if (isUpc) { - hfEvSel.addHistograms(qaRegistry); // collision monitoring - } - ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -732,127 +687,6 @@ struct HfTaskLc { } } - template - void runAnalysisPerCollisionDataWithUpc(CollType const& collisions, - CandType const& candidates, - BCsType const& bcs, - aod::FT0s const& ft0s, - aod::FV0As const& fv0as, - aod::FDDs const& fdds - - ) - { - for (const auto& collision : collisions) { - float centrality{-1.f}; - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc(collision, centrality, ccdb, qaRegistry, bcs); - if (rejectionMask != 0) { - /// at least one event selection not satisfied --> reject the candidate - continue; - } - const auto thisCollId = collision.globalIndex(); - const auto& groupedLcCandidates = candidates.sliceBy(candLcPerCollision, thisCollId); - const auto numPvContributors = collision.numContrib(); - const auto& bc = collision.template bc_as(); - - // Determine gap type using SGSelector with BC range checking - const auto gapResult = hf_upc::determineGapType(collision, bcs, upcThresholds); - const int gap = gapResult.value; - - // Use the BC with FIT activity if available from SGSelector - auto bcForUPC = bc; - if (gapResult.bc) { - bcForUPC = *(gapResult.bc); - } - - // Get FIT information from the UPC BC - upchelpers::FITInfo fitInfo{}; - udhelpers::getFITinfo(fitInfo, bcForUPC, bcs, ft0s, fv0as, fdds); - - // Get ZDC energies if available (extract once and reuse) - const bool hasZdc = bcForUPC.has_zdc(); - float zdcEnergyZNA = -1.f; - float zdcEnergyZNC = -1.f; - float zdcTimeZNA = -1.f; - float zdcTimeZNC = -1.f; - - if (hasZdc) { - const auto zdc = bcForUPC.zdc(); - zdcEnergyZNA = zdc.energyCommonZNA(); - zdcEnergyZNC = zdc.energyCommonZNC(); - zdcTimeZNA = zdc.timeZNA(); - zdcTimeZNC = zdc.timeZNC(); - qaRegistry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C); - qaRegistry.fill(HIST("Data/zdc/energyZNA_vs_energyZNC"), zdcEnergyZNA, zdcEnergyZNC); - qaRegistry.fill(HIST("Data/zdc/timeZNA_vs_timeZNC"), zdcTimeZNA, zdcTimeZNC); - qaRegistry.fill(HIST("Data/hUpcGapAfterSelection"), static_cast(gap)); - } - for (const auto& candidate : groupedLcCandidates) { - if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { - continue; - } - if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { - continue; - } - const auto pt = candidate.pt(); - const auto ptProng0 = candidate.ptProng0(); - const auto ptProng1 = candidate.ptProng1(); - const auto ptProng2 = candidate.ptProng2(); - const auto decayLength = candidate.decayLength(); - const auto chi2PCA = candidate.chi2PCA(); - const auto cpa = candidate.cpa(); - if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { - qaRegistry.fill(HIST("Data/hUpcMulti"), collision.multNTracksPV()); - qaRegistry.fill(HIST("Data/hUpcVtz"), collision.posZ()); - } - - if (fillTHn) { - double outputBkg(-1), outputPrompt(-1), outputFD(-1); - - auto fillTHnData = [&](bool isPKPi) { - const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); - - if constexpr (FillMl) { - const auto& mlProb = isPKPi ? candidate.mlProbLcToPKPi() : candidate.mlProbLcToPiKP(); - if (mlProb.size() == NumberOfMlClasses) { - outputBkg = mlProb[MlClassBackground]; /// bkg score - outputPrompt = mlProb[MlClassPrompt]; /// prompt score - outputFD = mlProb[MlClassNonPrompt]; /// non-prompt score - } - /// Fill the ML outputScores and variables of candidate - if (fillUPCTHnLite) { - if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { - std::vector valuesToFill{massLc, pt, outputBkg, outputPrompt, outputFD, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVarsWithBdt"))->Fill(valuesToFill.data()); - } - } else { - std::vector valuesToFill{massLc, pt, outputBkg, outputPrompt, outputFD, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVarsWithBdt"))->Fill(valuesToFill.data()); - } - - } else { - if (fillUPCTHnLite) { - if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { - std::vector valuesToFill{massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVars"))->Fill(valuesToFill.data()); - } - } else { - std::vector valuesToFill{massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVars"))->Fill(valuesToFill.data()); - } - } - }; - - if (candidate.isSelLcToPKPi() >= selectionFlagLc) { - fillTHnData(true); - } - if (candidate.isSelLcToPiKP() >= selectionFlagLc) { - fillTHnData(false); - } - } - } - } - } - /// Run the analysis on MC data /// \tparam FillMl switch to fill ML histograms template @@ -916,32 +750,6 @@ struct HfTaskLc { } PROCESS_SWITCH(HfTaskLc, processDataWithMlWithFT0M, "Process real data with the ML method and with FT0M centrality", false); - void processDataWithMlWithUpc(soa::Join const& collisions, - aod::BcFullInfos const& bcs, - LcCandidatesMl const& selectedLcCandidatesMl, - aod::Tracks const&, - aod::FT0s const& ft0s, - aod::FV0As const& fv0as, - aod::FDDs const& fdds, - aod::Zdcs const& /*zdcs*/) - { - runAnalysisPerCollisionDataWithUpc(collisions, selectedLcCandidatesMl, bcs, ft0s, fv0as, fdds); - } - PROCESS_SWITCH(HfTaskLc, processDataWithMlWithUpc, "Process real data with the ML method with UPC", false); - - void processDataStdWithUpc(soa::Join const& collisions, - aod::BcFullInfos const& bcs, - LcCandidatesMl const& selectedLcCandidatesMl, - aod::Tracks const&, - aod::FT0s const& ft0s, - aod::FV0As const& fv0as, - aod::FDDs const& fdds, - aod::Zdcs const& /*zdcs*/) - { - runAnalysisPerCollisionDataWithUpc(collisions, selectedLcCandidatesMl, bcs, ft0s, fv0as, fdds); - } - PROCESS_SWITCH(HfTaskLc, processDataStdWithUpc, "Process real data with the standard method with UPC", false); - void processMcStd(CollisionsMc const& collisions, LcCandidatesMc const& selectedLcCandidatesMc, McParticles3ProngMatched const& mcParticles,