From 041560bd2c1c142e70f9ad5ddd7e11ca92ca0d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 27 May 2025 12:09:07 +0200 Subject: [PATCH 1/3] A3: add qa tasks --- ALICE3/Tasks/CMakeLists.txt | 12 +++- ALICE3/Tasks/alice3Efficiency.cxx | 80 +++++++++++++++++++++ ALICE3/Tasks/pidSeparationPower.cxx | 106 ++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 ALICE3/Tasks/alice3Efficiency.cxx create mode 100644 ALICE3/Tasks/pidSeparationPower.cxx diff --git a/ALICE3/Tasks/CMakeLists.txt b/ALICE3/Tasks/CMakeLists.txt index 0b2e9972aa5..d19670d6aad 100644 --- a/ALICE3/Tasks/CMakeLists.txt +++ b/ALICE3/Tasks/CMakeLists.txt @@ -39,6 +39,11 @@ o2physics_add_dpl_workflow(alice3-pid-ftof-qa PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(alice3-pid-separation-power + SOURCES pidSeparationPower.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(alice3-cdeuteron SOURCES alice3-cdeuteron.cxx PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore @@ -52,4 +57,9 @@ o2physics_add_dpl_workflow(alice3-dilepton o2physics_add_dpl_workflow(alice3-taskcorrelationddbar SOURCES alice3-taskcorrelationDDbar.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) \ No newline at end of file + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(alice3-efficiency + SOURCES alice3Efficiency.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/ALICE3/Tasks/alice3Efficiency.cxx b/ALICE3/Tasks/alice3Efficiency.cxx new file mode 100644 index 00000000000..27a2e8bf4a7 --- /dev/null +++ b/ALICE3/Tasks/alice3Efficiency.cxx @@ -0,0 +1,80 @@ +// 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 alice3Efficiency.cxx +/// +/// \brief This task produces the efficiency +/// +/// \author Nicolò Jacazio, Universita del Piemonte Orientale (IT) +/// \since May 27, 2025 +/// + +#include +#include + +#include "Framework/AnalysisTask.h" +#include "Framework/runDataProcessing.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/ConfigParamRegistry.h" +#include "TEfficiency.h" +#include "THashList.h" + +using namespace o2; +using namespace o2::framework; +std::map effVsPt; + +struct alice3Efficiency { + Configurable> pdgCodes{"pdgCodes", {211}, "List of PDG codes to consider for efficiency calculation"}; + OutputObj outList{"output"}; + void init(o2::framework::InitContext&) + { + outList.setObject(new THashList); + for (auto pdg : pdgCodes.value) { + effVsPt[pdg] = new TEfficiency(Form("efficiency_pdg%d", pdg), + Form("Efficiency for PDG %d; p_{T} (GeV/c); Efficiency", pdg), + 100, 0, 10); + outList->Add(effVsPt[pdg]); + } + } + + void process(soa::Join const& tracks, + aod::McParticles const& mcParticles) + { + std::map> pdgIndices; + for (auto& mc : mcParticles) { + if (effVsPt.find(mc.pdgCode()) == effVsPt.end()) { + continue; + } + pdgIndices[mc.pdgCode()].push_back(mc.globalIndex()); + } + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto& mcParticle = track.mcParticle(); + // Check that the PDG is in the list of PDGs + if (pdgIndices.find(mcParticle.pdgCode()) == pdgIndices.end()) { + continue; + } + + std::vector& indices = pdgIndices[mcParticle.pdgCode()]; + // Fill efficiency histogram + auto& eff = effVsPt[mcParticle.pdgCode()]; + const bool found = std::find(indices.begin(), indices.end(), track.globalIndex()) != indices.end(); + eff->Fill(track.pt(), found); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& ctx) +{ + return WorkflowSpec{adaptAnalysisTask(ctx)}; +} diff --git a/ALICE3/Tasks/pidSeparationPower.cxx b/ALICE3/Tasks/pidSeparationPower.cxx new file mode 100644 index 00000000000..0f193370e60 --- /dev/null +++ b/ALICE3/Tasks/pidSeparationPower.cxx @@ -0,0 +1,106 @@ +// 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 pidSeparationPower.cxx +/// +/// \brief This task produces the separation power of the ALICE3 detector +/// +/// \author Nicolò Jacazio, Universita del Piemonte Orientale (IT) +/// \since May 13, 2025 +/// + +#include +#include +#include +#include + +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "Framework/runDataProcessing.h" +#include "Framework/RunningWorkflowInfo.h" +#include "Framework/HistogramRegistry.h" +#include "TProfile2D.h" +#include "THashList.h" +#include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/OTFRICH.h" + +using namespace o2; +using namespace o2::framework; + +std::array separationInnerTOF; +std::array separationOuterTOF; +std::array separationRICH; +struct pidSeparationPower { + + ConfigurableAxis etaAxis{"etaAxis", {100, -1.f, 1.f}, "Binning in eta"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + OutputObj listSeparation{"separationPower"}; + void init(o2::framework::InitContext&) + { + listSeparation.setObject(new THashList); + for (int i = 0; i < 5; i++) { + auto createEfficiency = [&](const char* name, const char* title) { + TProfile2D* eff = new TProfile2D(Form("%s_%d", name, i), + Form("%s_%d;%s", title, i, "#it{p}_{T} (GeV/#it{c});#it{#eta}"), + 100, 0.f, 10.f, + 100, 0.f, 10.f); + listSeparation->Add(eff); + return eff; + }; + separationInnerTOF[i] = createEfficiency("separationInnerTOF", "separationInnerTOF"); + separationOuterTOF[i] = createEfficiency("separationOuterTOF", "separationOuterTOF"); + separationRICH[i] = createEfficiency("separationRICH", "separationRICH"); + } + } + + void process(soa::Join::iterator const& /*collision*/, + soa::Join const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + // Check that all the nsigmas are numbers (sanity check) + for (int i = 0; i < 5; i++) { + if (std::isnan(track.nSigmaInnerTOF(i)) || std::isnan(track.nSigmaOuterTOF(i))) { + LOG(fatal) << "Unrecognized nsigma for " << i << " " << track.nSigmaInnerTOF(i) << " " << track.nSigmaOuterTOF(i); + } + } + + const auto& mcParticle = track.mcParticle(); + // Separation electron pion + switch (std::abs(mcParticle.pdgCode())) { + { + case 211: // electron-pion separation + separationInnerTOF[0]->Fill(track.pt(), track.eta(), track.nSigmaInnerTOF(0)); + separationOuterTOF[0]->Fill(track.pt(), track.eta(), track.nSigmaOuterTOF(0)); + // separationRICH[0]->Fill(track.pt(), track.eta(), track.nSigmaElectronRich() ); + break; + case 321: // pion-kaon separation + separationInnerTOF[1]->Fill(track.pt(), track.eta(), track.nSigmaInnerTOF(1)); + separationOuterTOF[1]->Fill(track.pt(), track.eta(), track.nSigmaInnerTOF(1)); + // separationRICH[1]->Fill(track.pt(), track.eta(), track.nSigmaPionRich() ); + break; + case 2212: // kaon-proton separation + separationInnerTOF[2]->Fill(track.pt(), track.eta(), track.nSigmaInnerTOF(2)); + separationOuterTOF[2]->Fill(track.pt(), track.eta(), track.nSigmaInnerTOF(2)); + // separationRICH[2]->Fill((track.nSigmaKaonRich() > 3.f), track.pt(), track.eta()); + default: + break; + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From 777bb8d2f6c23f4c01922fc3b93ef69aff9ef3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 27 May 2025 14:24:44 +0200 Subject: [PATCH 2/3] Update --- ALICE3/Tasks/CMakeLists.txt | 2 +- ALICE3/Tasks/alice3Efficiency.cxx | 18 ++++++++++++------ ...tionPower.cxx => alice3SeparationPower.cxx} | 6 +++--- 3 files changed, 16 insertions(+), 10 deletions(-) rename ALICE3/Tasks/{pidSeparationPower.cxx => alice3SeparationPower.cxx} (96%) diff --git a/ALICE3/Tasks/CMakeLists.txt b/ALICE3/Tasks/CMakeLists.txt index d19670d6aad..ddf9008e667 100644 --- a/ALICE3/Tasks/CMakeLists.txt +++ b/ALICE3/Tasks/CMakeLists.txt @@ -40,7 +40,7 @@ o2physics_add_dpl_workflow(alice3-pid-ftof-qa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(alice3-pid-separation-power - SOURCES pidSeparationPower.cxx + SOURCES alice3SeparationPower.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/ALICE3/Tasks/alice3Efficiency.cxx b/ALICE3/Tasks/alice3Efficiency.cxx index 27a2e8bf4a7..24d5abfe127 100644 --- a/ALICE3/Tasks/alice3Efficiency.cxx +++ b/ALICE3/Tasks/alice3Efficiency.cxx @@ -30,6 +30,7 @@ using namespace o2; using namespace o2::framework; std::map effVsPt; +std::map effVsEta; struct alice3Efficiency { Configurable> pdgCodes{"pdgCodes", {211}, "List of PDG codes to consider for efficiency calculation"}; @@ -37,11 +38,16 @@ struct alice3Efficiency { void init(o2::framework::InitContext&) { outList.setObject(new THashList); + auto createEff = [&](const char* baseName, const char* axisTitle, int pdg, int nBins, double min, double max) { + auto eff = new TEfficiency(Form("%s_pdg%d", baseName, pdg), + Form("Efficiency for PDG %d; %s; Efficiency", pdg, axisTitle), + nBins, min, max); + outList->Add(eff); + return eff; + }; for (auto pdg : pdgCodes.value) { - effVsPt[pdg] = new TEfficiency(Form("efficiency_pdg%d", pdg), - Form("Efficiency for PDG %d; p_{T} (GeV/c); Efficiency", pdg), - 100, 0, 10); - outList->Add(effVsPt[pdg]); + effVsPt[pdg] = createEff("efficiency", "p_{T} (GeV/c)", pdg, 100, 0, 10); + effVsEta[pdg] = createEff("efficiency_eta", "#eta", pdg, 100, -5, 5); } } @@ -67,9 +73,9 @@ struct alice3Efficiency { std::vector& indices = pdgIndices[mcParticle.pdgCode()]; // Fill efficiency histogram - auto& eff = effVsPt[mcParticle.pdgCode()]; const bool found = std::find(indices.begin(), indices.end(), track.globalIndex()) != indices.end(); - eff->Fill(track.pt(), found); + effVsPt[mcParticle.pdgCode()]->Fill(found, track.pt()); + effVsEta[mcParticle.pdgCode()]->Fill(found, track.eta()); } } }; diff --git a/ALICE3/Tasks/pidSeparationPower.cxx b/ALICE3/Tasks/alice3SeparationPower.cxx similarity index 96% rename from ALICE3/Tasks/pidSeparationPower.cxx rename to ALICE3/Tasks/alice3SeparationPower.cxx index 0f193370e60..ae5b52d0692 100644 --- a/ALICE3/Tasks/pidSeparationPower.cxx +++ b/ALICE3/Tasks/alice3SeparationPower.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \file pidSeparationPower.cxx +/// \file alice3SeparationPower.cxx /// /// \brief This task produces the separation power of the ALICE3 detector /// @@ -38,7 +38,7 @@ using namespace o2::framework; std::array separationInnerTOF; std::array separationOuterTOF; std::array separationRICH; -struct pidSeparationPower { +struct alice3SeparationPower { ConfigurableAxis etaAxis{"etaAxis", {100, -1.f, 1.f}, "Binning in eta"}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -103,4 +103,4 @@ struct pidSeparationPower { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From a1d319c56944944df286b3d3e13c9a158803b5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 27 May 2025 14:57:16 +0200 Subject: [PATCH 3/3] Update alice3Efficiency.cxx --- ALICE3/Tasks/alice3Efficiency.cxx | 39 ++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/ALICE3/Tasks/alice3Efficiency.cxx b/ALICE3/Tasks/alice3Efficiency.cxx index 24d5abfe127..7741ea24696 100644 --- a/ALICE3/Tasks/alice3Efficiency.cxx +++ b/ALICE3/Tasks/alice3Efficiency.cxx @@ -35,6 +35,7 @@ std::map effVsEta; struct alice3Efficiency { Configurable> pdgCodes{"pdgCodes", {211}, "List of PDG codes to consider for efficiency calculation"}; OutputObj outList{"output"}; + Configurable> etaRange{"etaRange", {-5.f, 5.f}, "Eta range for efficiency calculation"}; void init(o2::framework::InitContext&) { outList.setObject(new THashList); @@ -55,27 +56,43 @@ struct alice3Efficiency { aod::McParticles const& mcParticles) { std::map> pdgIndices; - for (auto& mc : mcParticles) { - if (effVsPt.find(mc.pdgCode()) == effVsPt.end()) { - continue; + + // Lambda function to select particles after all cuts + auto isParticleSelected = [&](const o2::aod::McParticle& p) { + if (!p.isPhysicalPrimary()) { + return false; } - pdgIndices[mc.pdgCode()].push_back(mc.globalIndex()); - } + if (p.eta() < etaRange.value.first) { + return false; + } + if (p.eta() > etaRange.value.second) { + return false; + } + return true; + }; for (const auto& track : tracks) { if (!track.has_mcParticle()) { continue; } const auto& mcParticle = track.mcParticle(); - // Check that the PDG is in the list of PDGs - if (pdgIndices.find(mcParticle.pdgCode()) == pdgIndices.end()) { + if (!isParticleSelected(mcParticle)) { continue; } + pdgIndices[mcParticle.pdgCode()].push_back(mcParticle.globalIndex()); + } - std::vector& indices = pdgIndices[mcParticle.pdgCode()]; + for (auto& mc : mcParticles) { + if (effVsPt.find(mc.pdgCode()) == effVsPt.end()) { + continue; + } + if (!isParticleSelected(mc)) { + continue; + } + std::vector& indices = pdgIndices[mc.pdgCode()]; // Fill efficiency histogram - const bool found = std::find(indices.begin(), indices.end(), track.globalIndex()) != indices.end(); - effVsPt[mcParticle.pdgCode()]->Fill(found, track.pt()); - effVsEta[mcParticle.pdgCode()]->Fill(found, track.eta()); + const bool found = std::find(indices.begin(), indices.end(), mc.globalIndex()) != indices.end(); + effVsPt[mc.pdgCode()]->Fill(found, mc.pt()); + effVsEta[mc.pdgCode()]->Fill(found, mc.eta()); } } };