From 03b567acf084f2fe834e4402fd4917391623dd72 Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Wed, 24 Feb 2021 17:00:21 +0100 Subject: [PATCH 01/18] Start of the trigger development Co-authored-by: Maximiliano Puccio --- Analysis/Tasks/CMakeLists.txt | 1 + Analysis/Tasks/Trigger/CMakeLists.txt | 15 +++ Analysis/Tasks/Trigger/nucleiTrigger.cxx | 112 +++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 Analysis/Tasks/Trigger/CMakeLists.txt create mode 100644 Analysis/Tasks/Trigger/nucleiTrigger.cxx diff --git a/Analysis/Tasks/CMakeLists.txt b/Analysis/Tasks/CMakeLists.txt index 39b3f394b20ef..2405172768604 100644 --- a/Analysis/Tasks/CMakeLists.txt +++ b/Analysis/Tasks/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(PWGUD) add_subdirectory(Utils) add_subdirectory(SkimmingTutorials) add_subdirectory(PID) +add_subdirectory(Trigger) o2_add_dpl_workflow(trackextension diff --git a/Analysis/Tasks/Trigger/CMakeLists.txt b/Analysis/Tasks/Trigger/CMakeLists.txt new file mode 100644 index 0000000000000..b933e2470903c --- /dev/null +++ b/Analysis/Tasks/Trigger/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + + +o2_add_dpl_workflow(nuclei-trigger + SOURCES nucleiTrigger.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/Trigger/nucleiTrigger.cxx b/Analysis/Tasks/Trigger/nucleiTrigger.cxx new file mode 100644 index 0000000000000..07afa3498b5a8 --- /dev/null +++ b/Analysis/Tasks/Trigger/nucleiTrigger.cxx @@ -0,0 +1,112 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. +// O2 includes + +#include "ReconstructionDataFormats/Track.h" +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoAHelpers.h" +#include "AnalysisDataModel/PID/PIDResponse.h" +#include "AnalysisDataModel/TrackSelectionTables.h" + +#include "AnalysisDataModel/EventSelection.h" +#include "AnalysisDataModel/TrackSelectionTables.h" +#include "AnalysisDataModel/Centrality.h" + +#include "Framework/HistogramRegistry.h" + +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct nucleiTrigger { + + HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + void init(o2::framework::InitContext&) + { + std::vector ptBinning = {0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.8, 3.2, 3.6, 4., 5.}; + std::vector centBinning = {0., 1., 5., 10., 20., 30., 40., 50., 70., 100.}; + + AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec centAxis = {centBinning, "V0M (%)"}; + + spectra.add("fCollZpos", "collision z position", HistType::kTH1F, {{600, -20., +20., "z position (cm)"}}); + spectra.add("fKeepEvent", "skimming histogram", HistType::kTH1F, {{2, -0.5, +1.5, "true: keep event, false: reject event"}}); + spectra.add("fTPCsignal", "Specific energy loss", HistType::kTH2F, {{600, 0., 3, "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); + spectra.add("fTPCcounts", "n-sigma TPC", HistType::kTH2F, {ptAxis, {200, -100., +100., "n#sigma_{He} (a. u.)"}}); + } + + Configurable yMin{"yMin", -0.8, "Maximum rapidity"}; + Configurable yMax{"yMax", 0.8, "Minimum rapidity"}; + Configurable yBeam{"yBeam", 0., "Beam rapidity"}; + + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable nsigmacutLow{"nsigmacutLow", -30.0, "Value of the Nsigma cut"}; + Configurable nsigmacutHigh{"nsigmacutHigh", +3., "Value of the Nsigma cut"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == (uint8_t) true); + + using TrackCandidates = soa::Filtered>; + + void process(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, TrackCandidates const& tracks) + { + // + // collision process loop + // + bool keepEvent = kFALSE; + if (!collision.alias()[kINT7]) { + return; + } + if (!collision.sel7()) { + return; + } + // + spectra.fill(HIST("fCollZpos"), collision.posZ()); + // + for (auto track : tracks) { // start loop over tracks + + TLorentzVector cutVector{}; + cutVector.SetPtEtaPhiM(track.pt() * 2.0, track.eta(), track.phi(), constants::physics::MassHelium3); + if (cutVector.Rapidity() < yMin + yBeam || cutVector.Rapidity() > yMax + yBeam) { + continue; + } + // + // fill QA histograms + // + spectra.fill(HIST("fTPCsignal"), track.tpcInnerParam(), track.tpcSignal()); + spectra.fill(HIST("fTPCcounts"), track.tpcInnerParam(), track.tpcNSigmaHe()); + // + // check offline-trigger (skimming) condidition + // + if (track.tpcNSigmaHe() > nsigmacutLow && track.tpcNSigmaHe() < nsigmacutHigh) { + keepEvent = kTRUE; + } + + } // end loop over tracks + // + // fill trigger (skimming) results + // + spectra.fill(HIST("fKeepEvent"), keepEvent); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + adaptAnalysisTask("nuclei-trigger")}; +} From 00654a318e7b61e76bcf8af8cea13d23184261b3 Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Wed, 24 Feb 2021 20:00:40 +0100 Subject: [PATCH 02/18] Implement single PID selections for each nucleus --- Analysis/Tasks/Trigger/CMakeLists.txt | 2 +- Analysis/Tasks/Trigger/nucleiTrigger.cxx | 64 ++++++++++++++++++------ 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/Analysis/Tasks/Trigger/CMakeLists.txt b/Analysis/Tasks/Trigger/CMakeLists.txt index b933e2470903c..90f6337489527 100644 --- a/Analysis/Tasks/Trigger/CMakeLists.txt +++ b/Analysis/Tasks/Trigger/CMakeLists.txt @@ -12,4 +12,4 @@ o2_add_dpl_workflow(nuclei-trigger SOURCES nucleiTrigger.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore - COMPONENT_NAME Analysis) + COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/Trigger/nucleiTrigger.cxx b/Analysis/Tasks/Trigger/nucleiTrigger.cxx index 07afa3498b5a8..2065fda4bb935 100644 --- a/Analysis/Tasks/Trigger/nucleiTrigger.cxx +++ b/Analysis/Tasks/Trigger/nucleiTrigger.cxx @@ -23,14 +23,37 @@ #include "Framework/HistogramRegistry.h" -#include - #include +#include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +namespace { + float rapidity(float pt, float m, float eta) { + return std::asinh(pt / std::hypot(m, pt) * std::sinh(eta)); + } + + static constexpr int nNuclei{4}; + static constexpr int nCutsPID{5}; + static constexpr std::array masses{ + constants::physics::MassDeuteron, constants::physics::MassTriton, + constants::physics::MassHelium3, constants::physics::MassAlpha + }; + static constexpr std::array charges{1, 1, 2, 2}; + static const std::string nucleiNames[nNuclei]{"H2", "H3", "He3", "He4"}; + static const std::string cutsNames[nCutsPID]{ + "TPCnSigmaMin", "TPCnSigmaMax", "TOFnSigmaMin", "TOFnSigmaMax", "TOFpidStartPt" + }; + static constexpr float cutsPID[nNuclei][nCutsPID]{ + {-3.f, +3.f, -4.f, +4.f, 1.0f}, /*H2*/ + {-3.f, +3.f, -4.f, +4.f, 1.6f}, /*H3*/ + {-5.f, +5.f, -4.f, +4.f, 14000.f}, /*He3*/ + {-5.f, +5.f, -4.f, +4.f, 14000.f} /*He4*/ + }; +} + struct nucleiTrigger { HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -55,9 +78,11 @@ struct nucleiTrigger { Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; - Configurable nsigmacutLow{"nsigmacutLow", -30.0, "Value of the Nsigma cut"}; + Configurable nsigmacutLow{"nsigmacutLow", -3., "Value of the Nsigma cut"}; Configurable nsigmacutHigh{"nsigmacutHigh", +3., "Value of the Nsigma cut"}; + Configurable> cfgCutsPID{"nucleiCutsPID", {cutsPID[0], nNuclei, nCutsPID, nucleiNames, cutsNames}, "Nuclei PID selections"}; + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == (uint8_t) true); @@ -78,24 +103,35 @@ struct nucleiTrigger { // spectra.fill(HIST("fCollZpos"), collision.posZ()); // + for (auto track : tracks) { // start loop over tracks - TLorentzVector cutVector{}; - cutVector.SetPtEtaPhiM(track.pt() * 2.0, track.eta(), track.phi(), constants::physics::MassHelium3); - if (cutVector.Rapidity() < yMin + yBeam || cutVector.Rapidity() > yMax + yBeam) { - continue; + const float nSigmaTPC[nNuclei]{ + track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl()} + ; + const float nSigmaTOF[nNuclei]{ + track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe(), track.tofNSigmaAl() + }; + + for (int iN{0}; iN < nNuclei; ++iN) { + float y{rapidity(track.pt() * charges[iN], masses[iN], track.eta())}; + if (y < yMin + yBeam || y > yMax + yBeam) { + continue; + } + if (nSigmaTPC[iN] < cfgCutsPID.get(iN, 0) || nSigmaTPC[iN] > cfgCutsPID.get(iN, 1)) { + continue; + } + if (track.pt() > cfgCutsPID.get(iN, 4) && (nSigmaTOF[iN] < cfgCutsPID.get(iN, 2) || nSigmaTOF[iN] > cfgCutsPID.get(iN, 3))) { + continue; + } + keepEvent = true; } + // // fill QA histograms // spectra.fill(HIST("fTPCsignal"), track.tpcInnerParam(), track.tpcSignal()); spectra.fill(HIST("fTPCcounts"), track.tpcInnerParam(), track.tpcNSigmaHe()); - // - // check offline-trigger (skimming) condidition - // - if (track.tpcNSigmaHe() > nsigmacutLow && track.tpcNSigmaHe() < nsigmacutHigh) { - keepEvent = kTRUE; - } } // end loop over tracks // @@ -108,5 +144,5 @@ struct nucleiTrigger { WorkflowSpec defineDataProcessing(ConfigContext const&) { return WorkflowSpec{ - adaptAnalysisTask("nuclei-trigger")}; + adaptAnalysisTask("nuclei-spectra")}; } From 6b8550ee378b6aada9d911f043855384ac891d57 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Wed, 24 Feb 2021 22:56:09 +0100 Subject: [PATCH 03/18] Fix compilation of the labeled array config --- Analysis/Tasks/Trigger/nucleiTrigger.cxx | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Analysis/Tasks/Trigger/nucleiTrigger.cxx b/Analysis/Tasks/Trigger/nucleiTrigger.cxx index 2065fda4bb935..6a07d6eb30547 100644 --- a/Analysis/Tasks/Trigger/nucleiTrigger.cxx +++ b/Analysis/Tasks/Trigger/nucleiTrigger.cxx @@ -42,8 +42,8 @@ namespace { constants::physics::MassHelium3, constants::physics::MassAlpha }; static constexpr std::array charges{1, 1, 2, 2}; - static const std::string nucleiNames[nNuclei]{"H2", "H3", "He3", "He4"}; - static const std::string cutsNames[nCutsPID]{ + static const std::vector nucleiNames{"H2", "H3", "He3", "He4"}; + static const std::vector cutsNames{ "TPCnSigmaMin", "TPCnSigmaMax", "TOFnSigmaMin", "TOFnSigmaMax", "TOFpidStartPt" }; static constexpr float cutsPID[nNuclei][nCutsPID]{ @@ -78,8 +78,6 @@ struct nucleiTrigger { Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; - Configurable nsigmacutLow{"nsigmacutLow", -3., "Value of the Nsigma cut"}; - Configurable nsigmacutHigh{"nsigmacutHigh", +3., "Value of the Nsigma cut"}; Configurable> cfgCutsPID{"nucleiCutsPID", {cutsPID[0], nNuclei, nCutsPID, nucleiNames, cutsNames}, "Nuclei PID selections"}; @@ -93,13 +91,7 @@ struct nucleiTrigger { // // collision process loop // - bool keepEvent = kFALSE; - if (!collision.alias()[kINT7]) { - return; - } - if (!collision.sel7()) { - return; - } + bool keepEvent = false; // spectra.fill(HIST("fCollZpos"), collision.posZ()); // @@ -118,10 +110,10 @@ struct nucleiTrigger { if (y < yMin + yBeam || y > yMax + yBeam) { continue; } - if (nSigmaTPC[iN] < cfgCutsPID.get(iN, 0) || nSigmaTPC[iN] > cfgCutsPID.get(iN, 1)) { + if (nSigmaTPC[iN] < cfgCutsPID->get(iN, 0u) || nSigmaTPC[iN] > cfgCutsPID->get(iN, 1u)) { continue; } - if (track.pt() > cfgCutsPID.get(iN, 4) && (nSigmaTOF[iN] < cfgCutsPID.get(iN, 2) || nSigmaTOF[iN] > cfgCutsPID.get(iN, 3))) { + if (track.pt() > cfgCutsPID->get(iN, 4u) && (nSigmaTOF[iN] < cfgCutsPID->get(iN, 2u) || nSigmaTOF[iN] > cfgCutsPID->get(iN, 3u))) { continue; } keepEvent = true; @@ -144,5 +136,5 @@ struct nucleiTrigger { WorkflowSpec defineDataProcessing(ConfigContext const&) { return WorkflowSpec{ - adaptAnalysisTask("nuclei-spectra")}; + adaptAnalysisTask("nuclei-trigger")}; } From 70a55ae3bc2503e399f287a473e6c2a5f23810f2 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Thu, 25 Feb 2021 01:38:30 +0100 Subject: [PATCH 04/18] Get an output trigger table --- Analysis/Tasks/Trigger/nucleiTrigger.cxx | 40 +++++++++++------------- Analysis/Tasks/Trigger/triggerTables.h | 31 ++++++++++++++++++ 2 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 Analysis/Tasks/Trigger/triggerTables.h diff --git a/Analysis/Tasks/Trigger/nucleiTrigger.cxx b/Analysis/Tasks/Trigger/nucleiTrigger.cxx index 6a07d6eb30547..9ed8d7e5e3fad 100644 --- a/Analysis/Tasks/Trigger/nucleiTrigger.cxx +++ b/Analysis/Tasks/Trigger/nucleiTrigger.cxx @@ -18,8 +18,7 @@ #include "AnalysisDataModel/TrackSelectionTables.h" #include "AnalysisDataModel/EventSelection.h" -#include "AnalysisDataModel/TrackSelectionTables.h" -#include "AnalysisDataModel/Centrality.h" +#include "triggerTables.h" #include "Framework/HistogramRegistry.h" @@ -31,7 +30,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; namespace { - float rapidity(float pt, float m, float eta) { + float rapidity(float pt, float eta, float m) { return std::asinh(pt / std::hypot(m, pt) * std::sinh(eta)); } @@ -56,6 +55,17 @@ namespace { struct nucleiTrigger { + Produces triggers; + + Configurable yMin{"yMin", -0.8, "Maximum rapidity"}; + Configurable yMax{"yMax", 0.8, "Minimum rapidity"}; + Configurable yBeam{"yBeam", 0., "Beam rapidity"}; + + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + + Configurable> cfgCutsPID{"nucleiCutsPID", {cutsPID[0], nNuclei, nCutsPID, nucleiNames, cutsNames}, "Nuclei PID selections"}; + HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; void init(o2::framework::InitContext&) @@ -67,31 +77,19 @@ struct nucleiTrigger { AxisSpec centAxis = {centBinning, "V0M (%)"}; spectra.add("fCollZpos", "collision z position", HistType::kTH1F, {{600, -20., +20., "z position (cm)"}}); - spectra.add("fKeepEvent", "skimming histogram", HistType::kTH1F, {{2, -0.5, +1.5, "true: keep event, false: reject event"}}); spectra.add("fTPCsignal", "Specific energy loss", HistType::kTH2F, {{600, 0., 3, "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); spectra.add("fTPCcounts", "n-sigma TPC", HistType::kTH2F, {ptAxis, {200, -100., +100., "n#sigma_{He} (a. u.)"}}); } - Configurable yMin{"yMin", -0.8, "Maximum rapidity"}; - Configurable yMax{"yMax", 0.8, "Minimum rapidity"}; - Configurable yBeam{"yBeam", 0., "Beam rapidity"}; - - Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; - Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; - - Configurable> cfgCutsPID{"nucleiCutsPID", {cutsPID[0], nNuclei, nCutsPID, nucleiNames, cutsNames}, "Nuclei PID selections"}; - Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == (uint8_t) true); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == 1u); using TrackCandidates = soa::Filtered>; void process(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, TrackCandidates const& tracks) { - // // collision process loop - // - bool keepEvent = false; + bool keepEvent[nNuclei]{false, false, false, false}; // spectra.fill(HIST("fCollZpos"), collision.posZ()); // @@ -106,7 +104,7 @@ struct nucleiTrigger { }; for (int iN{0}; iN < nNuclei; ++iN) { - float y{rapidity(track.pt() * charges[iN], masses[iN], track.eta())}; + float y{rapidity(track.pt() * charges[iN], track.eta(), masses[iN])}; if (y < yMin + yBeam || y > yMax + yBeam) { continue; } @@ -116,7 +114,7 @@ struct nucleiTrigger { if (track.pt() > cfgCutsPID->get(iN, 4u) && (nSigmaTOF[iN] < cfgCutsPID->get(iN, 2u) || nSigmaTOF[iN] > cfgCutsPID->get(iN, 3u))) { continue; } - keepEvent = true; + keepEvent[iN] = true; } // @@ -127,9 +125,7 @@ struct nucleiTrigger { } // end loop over tracks // - // fill trigger (skimming) results - // - spectra.fill(HIST("fKeepEvent"), keepEvent); + triggers(keepEvent[0], keepEvent[1], keepEvent[2], keepEvent[3]); } }; diff --git a/Analysis/Tasks/Trigger/triggerTables.h b/Analysis/Tasks/Trigger/triggerTables.h new file mode 100644 index 0000000000000..df047ff411307 --- /dev/null +++ b/Analysis/Tasks/Trigger/triggerTables.h @@ -0,0 +1,31 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. +#ifndef O2_ANALYSIS_TRIGGER_H_ +#define O2_ANALYSIS_TRIGGER_H_ + +#include "Framework/AnalysisDataModel.h" + +namespace o2::aod +{ +namespace trigger +{ +DECLARE_SOA_COLUMN(H2, hasH2, bool); +DECLARE_SOA_COLUMN(H3, hasH3, bool); +DECLARE_SOA_COLUMN(He3, hasHe3, bool); +DECLARE_SOA_COLUMN(He4, hasHe4, bool); + +} // namespace trigger + +DECLARE_SOA_TABLE(NucleiTriggers, "AOD", "Nuclei Triggers", trigger::H2, trigger::H3, trigger::He3, trigger::He4); + +using NucleiTrigger = NucleiTriggers::iterator; +} // namespace o2::aod + +#endif // O2_ANALYSIS_TRIGGER_H_ From 55ec20b73cea80bbf4b6428b0bbc20d88a0d9734 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Fri, 26 Mar 2021 19:35:05 +0100 Subject: [PATCH 05/18] Software triggers are now called Event Filters ... and the software sits outside the analysis task directory --- Analysis/CMakeLists.txt | 1 + .../{Tasks/Trigger => EventFiltering}/CMakeLists.txt | 4 ++-- .../filterTables.h} | 8 ++++---- .../nucleiFilter.cxx} | 12 ++++++------ Analysis/Tasks/CMakeLists.txt | 1 - 5 files changed, 13 insertions(+), 13 deletions(-) rename Analysis/{Tasks/Trigger => EventFiltering}/CMakeLists.txt (88%) rename Analysis/{Tasks/Trigger/triggerTables.h => EventFiltering/filterTables.h} (79%) rename Analysis/{Tasks/Trigger/nucleiTrigger.cxx => EventFiltering/nucleiFilter.cxx} (94%) diff --git a/Analysis/CMakeLists.txt b/Analysis/CMakeLists.txt index 922465bb4fc3c..bbca46e0aca02 100644 --- a/Analysis/CMakeLists.txt +++ b/Analysis/CMakeLists.txt @@ -14,3 +14,4 @@ add_subdirectory(Tasks) add_subdirectory(Tutorials) add_subdirectory(PWGDQ) add_subdirectory(ALICE3) +add_subdirectory(EventFiltering) diff --git a/Analysis/Tasks/Trigger/CMakeLists.txt b/Analysis/EventFiltering/CMakeLists.txt similarity index 88% rename from Analysis/Tasks/Trigger/CMakeLists.txt rename to Analysis/EventFiltering/CMakeLists.txt index 90f6337489527..0a9f1abcc33eb 100644 --- a/Analysis/Tasks/Trigger/CMakeLists.txt +++ b/Analysis/EventFiltering/CMakeLists.txt @@ -9,7 +9,7 @@ # submit itself to any jurisdiction. -o2_add_dpl_workflow(nuclei-trigger - SOURCES nucleiTrigger.cxx +o2_add_dpl_workflow(nuclei-filter + SOURCES nucleiFilter.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore COMPONENT_NAME Analysis) diff --git a/Analysis/Tasks/Trigger/triggerTables.h b/Analysis/EventFiltering/filterTables.h similarity index 79% rename from Analysis/Tasks/Trigger/triggerTables.h rename to Analysis/EventFiltering/filterTables.h index df047ff411307..7a1dac43f379a 100644 --- a/Analysis/Tasks/Trigger/triggerTables.h +++ b/Analysis/EventFiltering/filterTables.h @@ -14,18 +14,18 @@ namespace o2::aod { -namespace trigger +namespace filtering { DECLARE_SOA_COLUMN(H2, hasH2, bool); DECLARE_SOA_COLUMN(H3, hasH3, bool); DECLARE_SOA_COLUMN(He3, hasHe3, bool); DECLARE_SOA_COLUMN(He4, hasHe4, bool); -} // namespace trigger +} // namespace filtering -DECLARE_SOA_TABLE(NucleiTriggers, "AOD", "Nuclei Triggers", trigger::H2, trigger::H3, trigger::He3, trigger::He4); +DECLARE_SOA_TABLE(NucleiFilters, "AOD", "Nuclei Filters", filtering::H2, filtering::H3, filtering::He3, filtering::He4); -using NucleiTrigger = NucleiTriggers::iterator; +using NucleiFilter = NucleiFilters::iterator; } // namespace o2::aod #endif // O2_ANALYSIS_TRIGGER_H_ diff --git a/Analysis/Tasks/Trigger/nucleiTrigger.cxx b/Analysis/EventFiltering/nucleiFilter.cxx similarity index 94% rename from Analysis/Tasks/Trigger/nucleiTrigger.cxx rename to Analysis/EventFiltering/nucleiFilter.cxx index 9ed8d7e5e3fad..6158c968c40fd 100644 --- a/Analysis/Tasks/Trigger/nucleiTrigger.cxx +++ b/Analysis/EventFiltering/nucleiFilter.cxx @@ -18,7 +18,7 @@ #include "AnalysisDataModel/TrackSelectionTables.h" #include "AnalysisDataModel/EventSelection.h" -#include "triggerTables.h" +#include "filterTables.h" #include "Framework/HistogramRegistry.h" @@ -53,9 +53,9 @@ namespace { }; } -struct nucleiTrigger { +struct nucleiFilter { - Produces triggers; + Produces tags; Configurable yMin{"yMin", -0.8, "Maximum rapidity"}; Configurable yMax{"yMax", 0.8, "Minimum rapidity"}; @@ -125,12 +125,12 @@ struct nucleiTrigger { } // end loop over tracks // - triggers(keepEvent[0], keepEvent[1], keepEvent[2], keepEvent[3]); + tags(keepEvent[0], keepEvent[1], keepEvent[2], keepEvent[3]); } }; -WorkflowSpec defineDataProcessing(ConfigContext const&) +WorkflowSpec defineDataProcessing(ConfigContext const& cfg) { return WorkflowSpec{ - adaptAnalysisTask("nuclei-trigger")}; + adaptAnalysisTask(cfg)}; } diff --git a/Analysis/Tasks/CMakeLists.txt b/Analysis/Tasks/CMakeLists.txt index 2405172768604..39b3f394b20ef 100644 --- a/Analysis/Tasks/CMakeLists.txt +++ b/Analysis/Tasks/CMakeLists.txt @@ -17,7 +17,6 @@ add_subdirectory(PWGUD) add_subdirectory(Utils) add_subdirectory(SkimmingTutorials) add_subdirectory(PID) -add_subdirectory(Trigger) o2_add_dpl_workflow(trackextension From b7ecb70ff9262a55b45bef1e3b973740df3d73cd Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Tue, 6 Apr 2021 10:34:14 +0200 Subject: [PATCH 06/18] First draft of cEFiP --- .../centralEventFilterProcessor.cxx | 103 +++++++ .../centralEventFilterProcessor.h | 43 +++ Analysis/EventFiltering/filterTables.h | 4 + Analysis/EventFiltering/run_tasks.sh | 280 ++++++++++++++++++ 4 files changed, 430 insertions(+) create mode 100644 Analysis/EventFiltering/centralEventFilterProcessor.cxx create mode 100644 Analysis/EventFiltering/centralEventFilterProcessor.h create mode 100644 Analysis/EventFiltering/run_tasks.sh diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.cxx b/Analysis/EventFiltering/centralEventFilterProcessor.cxx new file mode 100644 index 0000000000000..b3a7192ad7227 --- /dev/null +++ b/Analysis/EventFiltering/centralEventFilterProcessor.cxx @@ -0,0 +1,103 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 centralEventFilterProcessor.cxx + +#include "centralEventFilterProcessor.h" + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" + +#include +#include +#include +#include + +using namespace o2::framework; +using namespace rapidjson; + +namespace { + Document readJsonFile(std::string& config) { + FILE* fp = fopen(config.data(), "rb"); + + char readBuffer[65536]; + FileReadStream is(fp, readBuffer, sizeof(readBuffer)); + + Document d; + d.ParseStream(is); + fclose(fp); + return d; + } +} + +namespace o2::aod::filtering +{ + +void CentralEventFilterProcessor::init(framework::InitContext& ic) +{ + Document d = readJsonFile(config); + const Value& workflows = d["workflows"]; + // JSON example + // { + // "subwagon_name" : "CentralEventFilterProcessor", + // "configuration" : { + // "NucleiFilters" : { + // "H2" : 0.1, + // "H3" : 0.3, + // "HE3" : 1., + // "HE4" : 1. + // } + // } + // } + for (auto& workflow : workflows) { + if (std::string_view(workflow["subwagon_name"]) == "CentralEventFilterProcessor") { + auto& config = workflow["configuration"]); + for (auto& filter : AvailableFilters) { + auto& filterConfig = config[filter]; + for (auto& node : filterConfig) { + mDownscaling[node.name] = node.value; + } + } + break; + } + } +} + +void CentralEventFilterProcessor::run(ProcessingContext& pc) +{ + +} + +DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config) +{ + + std::vector inputs; + for (auto& workflow : workflows) { + for (unsigned int iFilter{0}; iFilter < AvailableFilters.size(); ++iFilter) { + if (std::string_view(workflow["subwagon_name"]) == std::string_view(AvailableFilters[iFilter])) { + inputs.emplace_back(AvailableFilters[iFilter], "AOD", FilterDescriptions[iFilter], 0, Lifetime::Timeframe); + break; + } + } + } + + std::vector outputs; + outputs.emplace_back("AOD", "Decision", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "o2-central-event-filter-processor", + inputs, + outputs, + AlgorithmSpec{adaptFromTask(config)}, + Options{ + {"filtering-config", VariantType::String, "", {"Path to the filtering json config file"}}}}; +} + +} // namespace o2::aod::filtering \ No newline at end of file diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.h b/Analysis/EventFiltering/centralEventFilterProcessor.h new file mode 100644 index 0000000000000..d1f07aefe3196 --- /dev/null +++ b/Analysis/EventFiltering/centralEventFilterProcessor.h @@ -0,0 +1,43 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 centralEventFilterProcessor.h + +#ifndef O2_CentralEventFilterProcessor +#define O2_CentralEventFilterProcessor + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" + +#include "filterTables.h" + +namespace o2::aod::filtering +{ + +class CentralEventFilterProcessor : public framework::Task +{ + public: + CentralEventFilterProcessor(const std::string &config) : mConfigFile{config} {} + ~CentralEventFilterProcessor() override = default; + void init(framework::InitContext& ic) final; + void run(framework::ProcessingContext& pc) final; + void endOfStream(framework::EndOfStreamContext& ec) final; + + private: + std::string mConfigFile; + std::unordered_map mDownscaling; +}; + +/// create a processor spec +framework::DataProcessorSpec getCentralEventFilterProcessorSpec(std::string &config); + +} // namespace o2::aod::filtering + +#endif /* O2_CentralEventFilterProcessor */ diff --git a/Analysis/EventFiltering/filterTables.h b/Analysis/EventFiltering/filterTables.h index 7a1dac43f379a..dbb740dcf2f9d 100644 --- a/Analysis/EventFiltering/filterTables.h +++ b/Analysis/EventFiltering/filterTables.h @@ -10,6 +10,7 @@ #ifndef O2_ANALYSIS_TRIGGER_H_ #define O2_ANALYSIS_TRIGGER_H_ +#include #include "Framework/AnalysisDataModel.h" namespace o2::aod @@ -25,6 +26,9 @@ DECLARE_SOA_COLUMN(He4, hasHe4, bool); DECLARE_SOA_TABLE(NucleiFilters, "AOD", "Nuclei Filters", filtering::H2, filtering::H3, filtering::He3, filtering::He4); +constexpr std::array AvailableFilters{"NucleiFilters"}; +constexpr std::array FilterDescriptions{"Nuclei Filters"}; + using NucleiFilter = NucleiFilters::iterator; } // namespace o2::aod diff --git a/Analysis/EventFiltering/run_tasks.sh b/Analysis/EventFiltering/run_tasks.sh new file mode 100644 index 0000000000000..a291cf7e0027f --- /dev/null +++ b/Analysis/EventFiltering/run_tasks.sh @@ -0,0 +1,280 @@ +#!/bin/bash + +FULL_CONFIG_JSON='full_config.json' +LDIR=$(pwd) +INPUT_DATA_FILENAME='input_data.txt' +XML_FILENAME='wn.xml' + +echo "*** BEGIN Printing environment ***" +env +ulimit -a +echo "*** END Printing environment ***" + +# Workaround to remove AliEn libraries. See https://alice.its.cern.ch/jira/browse/JAL-163 +export LD_LIBRARY_PATH=$(echo "$LD_LIBRARY_PATH" | tr : \\n | grep -vi 'alien.*2-19' | paste -sd:) + +# Workaround for el6 libraries in 8 core queues +# export LD_LIBRARY_PATH=$(echo "$LD_LIBRARY_PATH" | tr : \\n | grep -vi 'el6-x86_64' | paste -sd:) + +### Converting wn.xml to input_data.txt + sed -rn 's/.*turl="([^"]*)".*/\1/p' $XML_FILENAME > $INPUT_DATA_FILENAME + if [ ! -s $INPUT_DATA_FILENAME ]; then + err_message="$INPUT_DATA_FILENAME file is missing, skipping the test execution" + >&2 echo $err_message; echo $err_message >> .alienValidation.trace + exit 1 + fi + test_number_of_input_files=`sed -rn 's/.*turl="alien:\/\/([^"]*)".*/\1/p' $XML_FILENAME | wc -l` +### ^^^ ### + +### Creating necessary json files and constructing train test command + # Reading JSON file + #inputdata=(`cat $FULL_CONFIG_JSON | jq -r '.inputdata[]'`) # When parsing with for loop + inputdata=$(cat $FULL_CONFIG_JSON | jq -r '.inputdata | map(select(. != "")) | join(",")') + + workflows=$(cat $FULL_CONFIG_JSON | jq -r '[.workflows[]]') + configurations=`echo $workflows | jq -r '[.[].configuration]'` + workflow_names=`echo $workflows | jq -r '[.[].workflow_name]'` + suffixes=`echo $workflows | jq -r '[.[].suffix]'` + total_tests=`echo $configurations | jq '. | length'`; + + derived_data=`jq -r '.derived_data' $FULL_CONFIG_JSON` + if [ "$derived_data" == "true" ]; then + output_configuration=`jq -r '.OutputDirector' $FULL_CONFIG_JSON` + if [ "$output_configuration" == "null" ]; then + err_message="OutputDirector tag missing." + >&2 echo $err_message; echo $err_message >> .alienValidation.trace + exit 1 + fi + echo "{ \"OutputDirector\": $output_configuration }" > OutputDirector.json + fi + + # If input data is empty saving status json as the test is failed + if [ -z "$inputdata" ]; then + err_message="The inputdata field is empty." + >&2 echo $err_message; echo $err_message >> .alienValidation.trace + exit 1 + fi +### ^^^ ### + +cpu_cores=`jq -r '.cpu_cores' $FULL_CONFIG_JSON` +let SHARED_MEMORY=$cpu_cores*1000000000+1500000000 +let RATE_LIMIT=$cpu_cores*500000000+500000000 +READERS=2 +if [ "$cpu_cores" -ge "4" ]; then + READERS=4 +fi + +echo "Running on $cpu_cores cores and configuring with shared memory $SHARED_MEMORY, rate limit $RATE_LIMIT, readers $READERS" + +### Generating the command + command="" + #if [ "$cpu_cores" -eq "8" ]; then + # command="echo | " + #fi + for (( i=0; i<$total_tests ; i++ )); do + [[ $total_tests > 1 ]] && index=$((i+1)) || index="" + # Filenames + CONFIG_JSON=configuration$index.json + WORKFLOW_JSON=workflow$index.json + # Reading the necessary values of json files + configuration=`echo "$configurations" | jq -r ".[$i]"` + workflow=`echo $workflows | jq -r "[.[].json.workflow[$i]]"` + metadata=`echo $workflows | jq -r "[.[].json.metadata[$i]]"` + workflow_name=`echo "$workflow_names" | jq -r ".[$i]"` + suffix=`echo "$suffixes" | jq -r ".[$i]"` + + # Writing config and workflow json files + jq -n "${configuration}" > $CONFIG_JSON + echo "{\"workflow\": $workflow, \"metadata\": $metadata }" > $WORKFLOW_JSON + + aod_file="" + #if [ "$i" -eq "0" ]; then + aod_file="--aod-file @$LDIR/$INPUT_DATA_FILENAME " + if [ "$derived_data" == "true" ]; then + aod_file="$aod_file --aod-writer-json OutputDirector.json" + fi + #fi + + echo "Adding $workflow_name with configuration $CONFIG_JSON and suffix $suffix" + command+="$workflow_name -b --configuration json:/$LDIR"/$CONFIG_JSON" --workflow-suffix $suffix --readers $READERS --resources-monitoring 10 --shm-segment-size $SHARED_MEMORY --aod-memory-rate-limit $RATE_LIMIT $aod_file --driver-client-backend stdout:// | " + done + + # Removing last pipe + command=${command::-2} +### ^^^ ### + +config_files=`ls $LDIR | grep "configuration.*\.json"` +workflow_files=`ls $LDIR | grep "workflow[0-9]*\.json"` +if [ ! -f "$FULL_CONFIG_JSON" ] || [ -z "$workflow_files" ] || [ -z "$config_files" ]; then + err_message="The configuration file(s) is(are) missing" + >&2 echo $err_message; echo $err_message >> .alienValidation.trace + exit 1 +fi + +# Workaround bug in JAliEn connect +unset ALIEN_PROC_ID + +# So far everything is smooth, we can run the test command +START_TIME=`date +%s` +echo "Trying to execute command: ${command}" +echo "" +eval $command +O2_EXITCODE=$? +END_TIME=`date +%s` +let test_wall_time=$END_TIME-$START_TIME + +echo "" +echo "O2 exited with $O2_EXITCODE" +echo "" + +echo "$O2_EXITCODE" > o2exitcode + +if [ "$O2_EXITCODE" -eq "0" ] && [ -s performanceMetrics.json ]; then + echo "" + echo "Parsing metrics..." + # Copied verbatim from grid/prepare_metrics.py + cat << EOF | /usr/bin/env python - $FULL_CONFIG_JSON $test_wall_time $test_number_of_input_files `hostname -f` + +import sys +import copy +import json +import os + +with open(sys.argv[1]) as f: + train = json.load(f) + +with open('performanceMetrics.json') as f: + data = json.load(f) + +aggregated = json.loads("{}") + +processed = [] + +def addWorkflow(configs, name): + global processed + if not name in aggregated: + aggregated[name] = {} + for key in configs: + # print("K", key) + processed.append(key) + + for metric in data[key]: + #print (metric) + + arr = data[key][metric] + + if not metric in aggregated[name]: + aggregated[name][metric] = copy.deepcopy(arr) + else: + if metric == "aod-file-read-info": # strings + for elem in arr: + aggregated[name][metric].append(elem) + else: + for idx, elem in enumerate(aggregated[name][metric]): + if idx < len(arr): + elem["value"] = float(elem["value"]) + float(arr[idx]["value"]) + + if metric == "cpuUsedAbsolute": + cpu_sum = 0 + for elem in arr: + cpu_sum += float(elem["value"]) + print("CPU seconds of %s: %d"%(key,cpu_sum/1e6)) + + +for workflow in train["workflows"]: + name = workflow["workflow_name"] + # print("W", name) + if not name in aggregated: + aggregated[name] = { "wagon_id": workflow["wagon_id"] } + addWorkflow(workflow["configuration"], name) + +addWorkflow([ "internal-dpl-aod-global-analysis-file-sink" ], "writer") + +reader_list = [] +for key in data: + if not key in processed: + # print(key) + reader_list.append(key) +addWorkflow(reader_list, "reader") + +# sum everything +full_train = {} +total_read = 0 +for key in data: + for metric in data[key]: + arr = data[key][metric] + + if metric == "aod-bytes-read-compressed": + total_read += int(arr[-1]["value"]) + + if metric != "aod-file-read-info": + if not metric in full_train: + full_train[metric] = copy.deepcopy(arr) + else: + for idx, elem in enumerate(full_train[metric]): + if idx < len(arr): + elem["value"] = float(elem["value"]) + float(arr[idx]["value"]) +aggregated["__full_train__"] = full_train + +output = json.loads("{}") +for key in aggregated: + print(key) + + cpu_sum = 0 + for elem in aggregated[key]["cpuUsedAbsolute"]: + cpu_sum += int(elem["value"]) + print("CPU seconds: %d"%(cpu_sum/1e6)) + output[key] = {} + if "wagon_id" in aggregated[key]: + output[key]["wagon_id"] = aggregated[key]["wagon_id"] + output[key]["cpu"] = [ cpu_sum/1e6 ] + + for metric in [ "proportionalSetSize" ]: + print(metric) + + arr = aggregated[key][metric] + + avg = 0 + maxval = 0 + for xy in arr: + avg += int(xy["value"]) + if maxval < int(xy["value"]): + maxval = int(xy["value"]) + + if len(arr) > 0: + avg /= len(arr) + + # convert to bytes + avg *= 1000 + maxval *= 1000 + + print("avg = %d B"%avg) + print("max = %d B"%maxval) + + output[key][metric + "_summary"] = { "avg" : [ avg ] , "max" : [ maxval ] } + +output["__full_train__"]["wall"] = [ int(sys.argv[2]) ] +output["__full_train__"]["number_of_input_files"] = [ int(sys.argv[3]) ] +output["__full_train__"]["aod-bytes-read-compressed"] = [ total_read ] +print("Read compressed bytes: %d"%total_read) + +output["reader"]["aod-file-read-info"] = aggregated["reader"]["aod-file-read-info"] +# append CE +alien_site = "" +if 'ALIEN_SITE' in os.environ: + os.environ['ALIEN_SITE'] +for elem in output["reader"]["aod-file-read-info"]: + elem["value"] += ",ce=" + alien_site + +output["__full_train__"]["alien_site"] = [ alien_site ] +output["__full_train__"]["host_name"] = [ sys.argv[4] ] + +with open('metrics_summary.json', 'w') as outfile: + json.dump(output, outfile) + +EOF + echo "" +fi + +#exit $? +exit 0 \ No newline at end of file From b5db34c394e53772661411f2c40ddabeef37cfd4 Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Thu, 8 Apr 2021 14:55:29 +0200 Subject: [PATCH 07/18] First version of CEFP workflow --- Analysis/EventFiltering/CMakeLists.txt | 9 ++++ Analysis/EventFiltering/cefp.cxx | 51 +++++++++++++++++++ .../centralEventFilterProcessor.cxx | 20 ++++---- .../centralEventFilterProcessor.h | 2 +- Analysis/EventFiltering/filterTables.h | 2 +- 5 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 Analysis/EventFiltering/cefp.cxx diff --git a/Analysis/EventFiltering/CMakeLists.txt b/Analysis/EventFiltering/CMakeLists.txt index 0a9f1abcc33eb..f1b60cdeb1b06 100644 --- a/Analysis/EventFiltering/CMakeLists.txt +++ b/Analysis/EventFiltering/CMakeLists.txt @@ -8,6 +8,15 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. +o2_add_library( + EventFiltering + SOURCES CentralEventFilterProcessor + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore) + +o2_add_executable(cefp + SOURCES cefp.cxx + COMPONENT_NAME Analysis + PUBLIC_LINK_LIBRARIES O2::EventFiltering) o2_add_dpl_workflow(nuclei-filter SOURCES nucleiFilter.cxx diff --git a/Analysis/EventFiltering/cefp.cxx b/Analysis/EventFiltering/cefp.cxx new file mode 100644 index 0000000000000..966273872d1b4 --- /dev/null +++ b/Analysis/EventFiltering/cefp.cxx @@ -0,0 +1,51 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. + +#include "CommonUtils/ConfigurableParam.h" +#include "centralEventFilterProcessor.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"config", o2::framework::VariantType::String, "sync", {}}}; + + std::swap(workflowOptions, options); + + std::string keyvaluehelp("Semicolon separated key=value"); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" +#include "Framework/Logger.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + auto config = configcontext.options().get("config"); + + if (config.empty()) { + LOG(FATAL) << "We need a configuration for the centralEventFilterProcessor"; + throw std::runtime_error("incompatible options provided"); + } + + WorkflowSpec specs; + specs.emplace_back(o2::aod::filtering::getCentralEventFilterProcessorSpec(config)); + return std::move(specs); +} diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.cxx b/Analysis/EventFiltering/centralEventFilterProcessor.cxx index b3a7192ad7227..5aea6fb1b0d56 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.cxx +++ b/Analysis/EventFiltering/centralEventFilterProcessor.cxx @@ -42,8 +42,6 @@ namespace o2::aod::filtering void CentralEventFilterProcessor::init(framework::InitContext& ic) { - Document d = readJsonFile(config); - const Value& workflows = d["workflows"]; // JSON example // { // "subwagon_name" : "CentralEventFilterProcessor", @@ -56,13 +54,14 @@ void CentralEventFilterProcessor::init(framework::InitContext& ic) // } // } // } - for (auto& workflow : workflows) { - if (std::string_view(workflow["subwagon_name"]) == "CentralEventFilterProcessor") { - auto& config = workflow["configuration"]); + Document d = readJsonFile(mConfigFile); + for (auto& workflow : d["workflows"].GetArray()) { + if (std::string_view(workflow["subwagon_name"].GetString()) == "CentralEventFilterProcessor") { + auto& config = workflow["configuration"]; for (auto& filter : AvailableFilters) { auto& filterConfig = config[filter]; - for (auto& node : filterConfig) { - mDownscaling[node.name] = node.value; + for (auto& node : filterConfig.GetObject()) { + mDownscaling[node.name.GetString()] = node.value.GetDouble(); } } break; @@ -79,10 +78,11 @@ DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config) { std::vector inputs; - for (auto& workflow : workflows) { + Document d = readJsonFile(config); + for (auto& workflow : d["workflows"].GetArray()) { for (unsigned int iFilter{0}; iFilter < AvailableFilters.size(); ++iFilter) { - if (std::string_view(workflow["subwagon_name"]) == std::string_view(AvailableFilters[iFilter])) { - inputs.emplace_back(AvailableFilters[iFilter], "AOD", FilterDescriptions[iFilter], 0, Lifetime::Timeframe); + if (std::string_view(workflow["subwagon_name"].GetString()) == std::string_view(AvailableFilters[iFilter])) { + inputs.emplace_back(std::string(AvailableFilters[iFilter]), "AOD", FilterDescriptions[iFilter], 0, Lifetime::Timeframe); break; } } diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.h b/Analysis/EventFiltering/centralEventFilterProcessor.h index d1f07aefe3196..502b4563571a4 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.h +++ b/Analysis/EventFiltering/centralEventFilterProcessor.h @@ -28,7 +28,7 @@ class CentralEventFilterProcessor : public framework::Task ~CentralEventFilterProcessor() override = default; void init(framework::InitContext& ic) final; void run(framework::ProcessingContext& pc) final; - void endOfStream(framework::EndOfStreamContext& ec) final; + void endOfStream(framework::EndOfStreamContext& ec) final {} private: std::string mConfigFile; diff --git a/Analysis/EventFiltering/filterTables.h b/Analysis/EventFiltering/filterTables.h index dbb740dcf2f9d..573e135b12e64 100644 --- a/Analysis/EventFiltering/filterTables.h +++ b/Analysis/EventFiltering/filterTables.h @@ -27,7 +27,7 @@ DECLARE_SOA_COLUMN(He4, hasHe4, bool); DECLARE_SOA_TABLE(NucleiFilters, "AOD", "Nuclei Filters", filtering::H2, filtering::H3, filtering::He3, filtering::He4); constexpr std::array AvailableFilters{"NucleiFilters"}; -constexpr std::array FilterDescriptions{"Nuclei Filters"}; +constexpr std::array FilterDescriptions{"Nuclei Filters"}; using NucleiFilter = NucleiFilters::iterator; } // namespace o2::aod From 68afea5bcf07570b176b91e523954fda9c500a2e Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Thu, 8 Apr 2021 14:59:25 +0200 Subject: [PATCH 08/18] Fixing naming in CMake file --- Analysis/EventFiltering/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Analysis/EventFiltering/CMakeLists.txt b/Analysis/EventFiltering/CMakeLists.txt index f1b60cdeb1b06..12a5bee37c2c3 100644 --- a/Analysis/EventFiltering/CMakeLists.txt +++ b/Analysis/EventFiltering/CMakeLists.txt @@ -10,7 +10,7 @@ o2_add_library( EventFiltering - SOURCES CentralEventFilterProcessor + SOURCES centralEventFilterProcessor.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore) o2_add_executable(cefp From ff67ba0e380c7b17bc4cb03de0dd23f78d9639e4 Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Thu, 8 Apr 2021 15:09:43 +0200 Subject: [PATCH 09/18] Fixing for compilation on Ubuntu --- Analysis/EventFiltering/nucleiFilter.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Analysis/EventFiltering/nucleiFilter.cxx b/Analysis/EventFiltering/nucleiFilter.cxx index 6158c968c40fd..b22851d37cb1a 100644 --- a/Analysis/EventFiltering/nucleiFilter.cxx +++ b/Analysis/EventFiltering/nucleiFilter.cxx @@ -82,7 +82,7 @@ struct nucleiFilter { } Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == 1u); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == static_cast(1u)); using TrackCandidates = soa::Filtered>; From 19db550b2352fdb657748a8c0441a9b3b5b5cc14 Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Thu, 8 Apr 2021 15:33:25 +0200 Subject: [PATCH 10/18] Fix command line options --- Analysis/EventFiltering/cefp.cxx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Analysis/EventFiltering/cefp.cxx b/Analysis/EventFiltering/cefp.cxx index 966273872d1b4..2f9abbfc3850f 100644 --- a/Analysis/EventFiltering/cefp.cxx +++ b/Analysis/EventFiltering/cefp.cxx @@ -19,13 +19,7 @@ using namespace o2::framework; void customize(std::vector& workflowOptions) { // option allowing to set parameters - std::vector options{ - {"config", o2::framework::VariantType::String, "sync", {}}}; - - std::swap(workflowOptions, options); - - std::string keyvaluehelp("Semicolon separated key=value"); - workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); + workflowOptions.push_back(ConfigParamSpec{"config", o2::framework::VariantType::String, "train_config.json", {"Configuration of the filtering"}}); } // ------------------------------------------------------------------ @@ -35,9 +29,6 @@ void customize(std::vector& workflowOptions) WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { - // Update the (declared) parameters if changed from the command line - o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - auto config = configcontext.options().get("config"); if (config.empty()) { From 33cb1e62635b0d6f1ff33fc5e59dd3608740082e Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Tue, 13 Apr 2021 18:37:43 +0200 Subject: [PATCH 11/18] First working version of CEFP with downscaling --- .../centralEventFilterProcessor.cxx | 88 +++++++++++++++++-- .../centralEventFilterProcessor.h | 11 ++- Analysis/EventFiltering/nucleiFilter.cxx | 6 ++ 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.cxx b/Analysis/EventFiltering/centralEventFilterProcessor.cxx index 5aea6fb1b0d56..f10d7d0e14d52 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.cxx +++ b/Analysis/EventFiltering/centralEventFilterProcessor.cxx @@ -14,7 +14,12 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/TableConsumer.h" +#include +#include + +#include #include #include #include @@ -54,24 +59,93 @@ void CentralEventFilterProcessor::init(framework::InitContext& ic) // } // } // } + std::cout << "Start init" << std::endl; Document d = readJsonFile(mConfigFile); + int nCols{0}; for (auto& workflow : d["workflows"].GetArray()) { if (std::string_view(workflow["subwagon_name"].GetString()) == "CentralEventFilterProcessor") { auto& config = workflow["configuration"]; for (auto& filter : AvailableFilters) { auto& filterConfig = config[filter]; - for (auto& node : filterConfig.GetObject()) { - mDownscaling[node.name.GetString()] = node.value.GetDouble(); + if (config.IsObject()) { + std::unordered_map tableMap; + for (auto& node : filterConfig.GetObject()) { + tableMap[node.name.GetString()] = node.value.GetDouble(); + nCols++; + } + mDownscaling[filter] = tableMap; } } break; } } + std::cout << "Middle init" << std::endl; + mScalers = new TH1D("mScalers",";;Number of events", nCols + 1, -0.5, 0.5 + nCols); + mScalers->GetXaxis()->SetBinLabel(1, "Total number of events"); + + mFiltered = new TH1D("mFiltered",";;Number of filtered events", nCols + 1, -0.5, 0.5 + nCols); + mFiltered->GetXaxis()->SetBinLabel(1, "Total number of events"); + + + int bin{2}; + for (auto& table : mDownscaling) { + for (auto& column : table.second) { + mScalers->GetXaxis()->SetBinLabel(bin, column.first.data()); + mFiltered->GetXaxis()->SetBinLabel(bin++, column.first.data()); + } + } + + TFile test("test.root","recreate"); + mScalers->Clone()->Write(); + test.Close(); } void CentralEventFilterProcessor::run(ProcessingContext& pc) { - + int64_t nEvents{-1}; + for (auto& tableName : mDownscaling) { + auto tableConsumer = pc.inputs().get(tableName.first); + + auto tablePtr{tableConsumer->asArrowTable()}; + int64_t nRows{tablePtr->num_rows()}; + nEvents = nEvents < 0 ? nRows : nEvents; + if (nEvents != nRows) { + std::cerr << "Inconsistent number of rows across trigger tables, fatal" << std::endl; ///TODO: move it to real fatal + } + + auto schema{tablePtr->schema()}; + for (auto& colName : tableName.second) { + int bin{mScalers->GetXaxis()->FindBin(colName.first.data())}; + double binCenter{mScalers->GetXaxis()->GetBinCenter(bin)}; + auto column{tablePtr->GetColumnByName(colName.first)}; + double downscaling{colName.second}; + if (column) { + for (int64_t iC{0}; iC < column->num_chunks(); ++iC) { + auto chunk{column->chunk(iC)}; + auto boolArray = std::static_pointer_cast(chunk); + for (int64_t iS{0}; iS < chunk->length(); ++iS) { + if (boolArray->Value(iS)) { + mScalers->Fill(binCenter); + if (mUniformGenerator(mGeneratorEngine) < downscaling) { + mFiltered->Fill(binCenter); + } + } + } + } + } + } + } + mScalers->SetBinContent(1, mScalers->GetBinContent(1) + nEvents); + mFiltered->SetBinContent(1, mFiltered->GetBinContent(1) + nEvents); + +} + +void CentralEventFilterProcessor::endOfStream(EndOfStreamContext& ec) +{ + TFile output("trigger.root","recreate"); + mScalers->Write(); + mFiltered->Write(); + output.Close(); } DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config) @@ -79,10 +153,12 @@ DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config) std::vector inputs; Document d = readJsonFile(config); + for (auto& workflow : d["workflows"].GetArray()) { for (unsigned int iFilter{0}; iFilter < AvailableFilters.size(); ++iFilter) { if (std::string_view(workflow["subwagon_name"].GetString()) == std::string_view(AvailableFilters[iFilter])) { inputs.emplace_back(std::string(AvailableFilters[iFilter]), "AOD", FilterDescriptions[iFilter], 0, Lifetime::Timeframe); + std::cout << "Adding input " << std::string_view(AvailableFilters[iFilter]) << std::endl; break; } } @@ -98,6 +174,8 @@ DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config) AlgorithmSpec{adaptFromTask(config)}, Options{ {"filtering-config", VariantType::String, "", {"Path to the filtering json config file"}}}}; -} +} + +} // namespace o2::aod::filtering -} // namespace o2::aod::filtering \ No newline at end of file +/// o2-analysis-cefp --config /Users/stefano.trogolo/alice/run3/O2/Analysis/EventFiltering/sample.json | o2-analysis-nuclei-filter --aod-file @listOfFiles.txt | o2-analysis-trackselection | o2-analysis-trackextension | o2-analysis-pid-tpc | o2-analysis-pid-tof | o2-analysis-event-selection | o2-analysis \ No newline at end of file diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.h b/Analysis/EventFiltering/centralEventFilterProcessor.h index 502b4563571a4..2381b5c549239 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.h +++ b/Analysis/EventFiltering/centralEventFilterProcessor.h @@ -16,8 +16,11 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include #include "filterTables.h" +class TH1D; + namespace o2::aod::filtering { @@ -28,11 +31,15 @@ class CentralEventFilterProcessor : public framework::Task ~CentralEventFilterProcessor() override = default; void init(framework::InitContext& ic) final; void run(framework::ProcessingContext& pc) final; - void endOfStream(framework::EndOfStreamContext& ec) final {} + void endOfStream(framework::EndOfStreamContext& ec) final; private: + TH1D* mScalers; + TH1D* mFiltered; std::string mConfigFile; - std::unordered_map mDownscaling; + std::unordered_map> mDownscaling; + std::mt19937_64 mGeneratorEngine; + std::uniform_real_distribution mUniformGenerator = std::uniform_real_distribution(0.,1.); }; /// create a processor spec diff --git a/Analysis/EventFiltering/nucleiFilter.cxx b/Analysis/EventFiltering/nucleiFilter.cxx index b22851d37cb1a..529938c891c6e 100644 --- a/Analysis/EventFiltering/nucleiFilter.cxx +++ b/Analysis/EventFiltering/nucleiFilter.cxx @@ -79,6 +79,7 @@ struct nucleiFilter { spectra.add("fCollZpos", "collision z position", HistType::kTH1F, {{600, -20., +20., "z position (cm)"}}); spectra.add("fTPCsignal", "Specific energy loss", HistType::kTH2F, {{600, 0., 3, "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); spectra.add("fTPCcounts", "n-sigma TPC", HistType::kTH2F, {ptAxis, {200, -100., +100., "n#sigma_{He} (a. u.)"}}); + spectra.add("fProcessedEvents", "Nuclei - event filtered", HistType::kTH1F,{{4 ,-0.5, 3.5, "Event counter"}}); } Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; @@ -125,6 +126,11 @@ struct nucleiFilter { } // end loop over tracks // + for (int iDecision{0}; iDecision < 4; ++iDecision) { + if (keepEvent[iDecision]) { + spectra.fill(HIST("fProcessedEvents"), iDecision); + } + } tags(keepEvent[0], keepEvent[1], keepEvent[2], keepEvent[3]); } }; From 6dfd9c6ab779d5898a2f2dbc5cbd941b932a5421 Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Tue, 20 Apr 2021 09:52:29 +0200 Subject: [PATCH 12/18] Add fraction of events counters --- Analysis/EventFiltering/centralEventFilterProcessor.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.cxx b/Analysis/EventFiltering/centralEventFilterProcessor.cxx index f10d7d0e14d52..88ec171d632bb 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.cxx +++ b/Analysis/EventFiltering/centralEventFilterProcessor.cxx @@ -143,8 +143,12 @@ void CentralEventFilterProcessor::run(ProcessingContext& pc) void CentralEventFilterProcessor::endOfStream(EndOfStreamContext& ec) { TFile output("trigger.root","recreate"); - mScalers->Write(); - mFiltered->Write(); + mScalers->Write("Scalers"); + mFiltered->Write("FilteredScalers"); + mScalers->Scale(1./mScalers->GetBinContent(1)); + mFiltered->Scale(1./mFiltered->GetBinContent(1)); + mScalers->Write("Fractions"); + mFiltered->Write("FractionsDownscaled"); output.Close(); } From 7bb07b779daea92cf584e8d4c6c7a85850833d45 Mon Sep 17 00:00:00 2001 From: Stefano Trogolo Date: Tue, 20 Apr 2021 09:57:29 +0200 Subject: [PATCH 13/18] Rename Trigger to EventFiltering --- Analysis/Tasks/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Analysis/Tasks/CMakeLists.txt b/Analysis/Tasks/CMakeLists.txt index 39b3f394b20ef..a4bbee7914e53 100644 --- a/Analysis/Tasks/CMakeLists.txt +++ b/Analysis/Tasks/CMakeLists.txt @@ -17,7 +17,7 @@ add_subdirectory(PWGUD) add_subdirectory(Utils) add_subdirectory(SkimmingTutorials) add_subdirectory(PID) - +add_subdirectory(EventFiltering) o2_add_dpl_workflow(trackextension SOURCES trackextension.cxx From 184bb55fd6222494ec32f7d612626c3b3ecdbbaa Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 20 Apr 2021 11:17:32 +0200 Subject: [PATCH 14/18] Use new PID framework and fix CMakeLists --- Analysis/EventFiltering/nucleiFilter.cxx | 3 +- Analysis/EventFiltering/run_tasks.sh | 280 ----------------------- Analysis/Tasks/CMakeLists.txt | 1 - 3 files changed, 1 insertion(+), 283 deletions(-) delete mode 100644 Analysis/EventFiltering/run_tasks.sh diff --git a/Analysis/EventFiltering/nucleiFilter.cxx b/Analysis/EventFiltering/nucleiFilter.cxx index 529938c891c6e..5e97a29d6b4b6 100644 --- a/Analysis/EventFiltering/nucleiFilter.cxx +++ b/Analysis/EventFiltering/nucleiFilter.cxx @@ -85,8 +85,7 @@ struct nucleiFilter { Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == static_cast(1u)); - using TrackCandidates = soa::Filtered>; - + using TrackCandidates = soa::Filtered>; void process(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, TrackCandidates const& tracks) { // collision process loop diff --git a/Analysis/EventFiltering/run_tasks.sh b/Analysis/EventFiltering/run_tasks.sh deleted file mode 100644 index a291cf7e0027f..0000000000000 --- a/Analysis/EventFiltering/run_tasks.sh +++ /dev/null @@ -1,280 +0,0 @@ -#!/bin/bash - -FULL_CONFIG_JSON='full_config.json' -LDIR=$(pwd) -INPUT_DATA_FILENAME='input_data.txt' -XML_FILENAME='wn.xml' - -echo "*** BEGIN Printing environment ***" -env -ulimit -a -echo "*** END Printing environment ***" - -# Workaround to remove AliEn libraries. See https://alice.its.cern.ch/jira/browse/JAL-163 -export LD_LIBRARY_PATH=$(echo "$LD_LIBRARY_PATH" | tr : \\n | grep -vi 'alien.*2-19' | paste -sd:) - -# Workaround for el6 libraries in 8 core queues -# export LD_LIBRARY_PATH=$(echo "$LD_LIBRARY_PATH" | tr : \\n | grep -vi 'el6-x86_64' | paste -sd:) - -### Converting wn.xml to input_data.txt - sed -rn 's/.*turl="([^"]*)".*/\1/p' $XML_FILENAME > $INPUT_DATA_FILENAME - if [ ! -s $INPUT_DATA_FILENAME ]; then - err_message="$INPUT_DATA_FILENAME file is missing, skipping the test execution" - >&2 echo $err_message; echo $err_message >> .alienValidation.trace - exit 1 - fi - test_number_of_input_files=`sed -rn 's/.*turl="alien:\/\/([^"]*)".*/\1/p' $XML_FILENAME | wc -l` -### ^^^ ### - -### Creating necessary json files and constructing train test command - # Reading JSON file - #inputdata=(`cat $FULL_CONFIG_JSON | jq -r '.inputdata[]'`) # When parsing with for loop - inputdata=$(cat $FULL_CONFIG_JSON | jq -r '.inputdata | map(select(. != "")) | join(",")') - - workflows=$(cat $FULL_CONFIG_JSON | jq -r '[.workflows[]]') - configurations=`echo $workflows | jq -r '[.[].configuration]'` - workflow_names=`echo $workflows | jq -r '[.[].workflow_name]'` - suffixes=`echo $workflows | jq -r '[.[].suffix]'` - total_tests=`echo $configurations | jq '. | length'`; - - derived_data=`jq -r '.derived_data' $FULL_CONFIG_JSON` - if [ "$derived_data" == "true" ]; then - output_configuration=`jq -r '.OutputDirector' $FULL_CONFIG_JSON` - if [ "$output_configuration" == "null" ]; then - err_message="OutputDirector tag missing." - >&2 echo $err_message; echo $err_message >> .alienValidation.trace - exit 1 - fi - echo "{ \"OutputDirector\": $output_configuration }" > OutputDirector.json - fi - - # If input data is empty saving status json as the test is failed - if [ -z "$inputdata" ]; then - err_message="The inputdata field is empty." - >&2 echo $err_message; echo $err_message >> .alienValidation.trace - exit 1 - fi -### ^^^ ### - -cpu_cores=`jq -r '.cpu_cores' $FULL_CONFIG_JSON` -let SHARED_MEMORY=$cpu_cores*1000000000+1500000000 -let RATE_LIMIT=$cpu_cores*500000000+500000000 -READERS=2 -if [ "$cpu_cores" -ge "4" ]; then - READERS=4 -fi - -echo "Running on $cpu_cores cores and configuring with shared memory $SHARED_MEMORY, rate limit $RATE_LIMIT, readers $READERS" - -### Generating the command - command="" - #if [ "$cpu_cores" -eq "8" ]; then - # command="echo | " - #fi - for (( i=0; i<$total_tests ; i++ )); do - [[ $total_tests > 1 ]] && index=$((i+1)) || index="" - # Filenames - CONFIG_JSON=configuration$index.json - WORKFLOW_JSON=workflow$index.json - # Reading the necessary values of json files - configuration=`echo "$configurations" | jq -r ".[$i]"` - workflow=`echo $workflows | jq -r "[.[].json.workflow[$i]]"` - metadata=`echo $workflows | jq -r "[.[].json.metadata[$i]]"` - workflow_name=`echo "$workflow_names" | jq -r ".[$i]"` - suffix=`echo "$suffixes" | jq -r ".[$i]"` - - # Writing config and workflow json files - jq -n "${configuration}" > $CONFIG_JSON - echo "{\"workflow\": $workflow, \"metadata\": $metadata }" > $WORKFLOW_JSON - - aod_file="" - #if [ "$i" -eq "0" ]; then - aod_file="--aod-file @$LDIR/$INPUT_DATA_FILENAME " - if [ "$derived_data" == "true" ]; then - aod_file="$aod_file --aod-writer-json OutputDirector.json" - fi - #fi - - echo "Adding $workflow_name with configuration $CONFIG_JSON and suffix $suffix" - command+="$workflow_name -b --configuration json:/$LDIR"/$CONFIG_JSON" --workflow-suffix $suffix --readers $READERS --resources-monitoring 10 --shm-segment-size $SHARED_MEMORY --aod-memory-rate-limit $RATE_LIMIT $aod_file --driver-client-backend stdout:// | " - done - - # Removing last pipe - command=${command::-2} -### ^^^ ### - -config_files=`ls $LDIR | grep "configuration.*\.json"` -workflow_files=`ls $LDIR | grep "workflow[0-9]*\.json"` -if [ ! -f "$FULL_CONFIG_JSON" ] || [ -z "$workflow_files" ] || [ -z "$config_files" ]; then - err_message="The configuration file(s) is(are) missing" - >&2 echo $err_message; echo $err_message >> .alienValidation.trace - exit 1 -fi - -# Workaround bug in JAliEn connect -unset ALIEN_PROC_ID - -# So far everything is smooth, we can run the test command -START_TIME=`date +%s` -echo "Trying to execute command: ${command}" -echo "" -eval $command -O2_EXITCODE=$? -END_TIME=`date +%s` -let test_wall_time=$END_TIME-$START_TIME - -echo "" -echo "O2 exited with $O2_EXITCODE" -echo "" - -echo "$O2_EXITCODE" > o2exitcode - -if [ "$O2_EXITCODE" -eq "0" ] && [ -s performanceMetrics.json ]; then - echo "" - echo "Parsing metrics..." - # Copied verbatim from grid/prepare_metrics.py - cat << EOF | /usr/bin/env python - $FULL_CONFIG_JSON $test_wall_time $test_number_of_input_files `hostname -f` - -import sys -import copy -import json -import os - -with open(sys.argv[1]) as f: - train = json.load(f) - -with open('performanceMetrics.json') as f: - data = json.load(f) - -aggregated = json.loads("{}") - -processed = [] - -def addWorkflow(configs, name): - global processed - if not name in aggregated: - aggregated[name] = {} - for key in configs: - # print("K", key) - processed.append(key) - - for metric in data[key]: - #print (metric) - - arr = data[key][metric] - - if not metric in aggregated[name]: - aggregated[name][metric] = copy.deepcopy(arr) - else: - if metric == "aod-file-read-info": # strings - for elem in arr: - aggregated[name][metric].append(elem) - else: - for idx, elem in enumerate(aggregated[name][metric]): - if idx < len(arr): - elem["value"] = float(elem["value"]) + float(arr[idx]["value"]) - - if metric == "cpuUsedAbsolute": - cpu_sum = 0 - for elem in arr: - cpu_sum += float(elem["value"]) - print("CPU seconds of %s: %d"%(key,cpu_sum/1e6)) - - -for workflow in train["workflows"]: - name = workflow["workflow_name"] - # print("W", name) - if not name in aggregated: - aggregated[name] = { "wagon_id": workflow["wagon_id"] } - addWorkflow(workflow["configuration"], name) - -addWorkflow([ "internal-dpl-aod-global-analysis-file-sink" ], "writer") - -reader_list = [] -for key in data: - if not key in processed: - # print(key) - reader_list.append(key) -addWorkflow(reader_list, "reader") - -# sum everything -full_train = {} -total_read = 0 -for key in data: - for metric in data[key]: - arr = data[key][metric] - - if metric == "aod-bytes-read-compressed": - total_read += int(arr[-1]["value"]) - - if metric != "aod-file-read-info": - if not metric in full_train: - full_train[metric] = copy.deepcopy(arr) - else: - for idx, elem in enumerate(full_train[metric]): - if idx < len(arr): - elem["value"] = float(elem["value"]) + float(arr[idx]["value"]) -aggregated["__full_train__"] = full_train - -output = json.loads("{}") -for key in aggregated: - print(key) - - cpu_sum = 0 - for elem in aggregated[key]["cpuUsedAbsolute"]: - cpu_sum += int(elem["value"]) - print("CPU seconds: %d"%(cpu_sum/1e6)) - output[key] = {} - if "wagon_id" in aggregated[key]: - output[key]["wagon_id"] = aggregated[key]["wagon_id"] - output[key]["cpu"] = [ cpu_sum/1e6 ] - - for metric in [ "proportionalSetSize" ]: - print(metric) - - arr = aggregated[key][metric] - - avg = 0 - maxval = 0 - for xy in arr: - avg += int(xy["value"]) - if maxval < int(xy["value"]): - maxval = int(xy["value"]) - - if len(arr) > 0: - avg /= len(arr) - - # convert to bytes - avg *= 1000 - maxval *= 1000 - - print("avg = %d B"%avg) - print("max = %d B"%maxval) - - output[key][metric + "_summary"] = { "avg" : [ avg ] , "max" : [ maxval ] } - -output["__full_train__"]["wall"] = [ int(sys.argv[2]) ] -output["__full_train__"]["number_of_input_files"] = [ int(sys.argv[3]) ] -output["__full_train__"]["aod-bytes-read-compressed"] = [ total_read ] -print("Read compressed bytes: %d"%total_read) - -output["reader"]["aod-file-read-info"] = aggregated["reader"]["aod-file-read-info"] -# append CE -alien_site = "" -if 'ALIEN_SITE' in os.environ: - os.environ['ALIEN_SITE'] -for elem in output["reader"]["aod-file-read-info"]: - elem["value"] += ",ce=" + alien_site - -output["__full_train__"]["alien_site"] = [ alien_site ] -output["__full_train__"]["host_name"] = [ sys.argv[4] ] - -with open('metrics_summary.json', 'w') as outfile: - json.dump(output, outfile) - -EOF - echo "" -fi - -#exit $? -exit 0 \ No newline at end of file diff --git a/Analysis/Tasks/CMakeLists.txt b/Analysis/Tasks/CMakeLists.txt index a4bbee7914e53..5d4dab653051c 100644 --- a/Analysis/Tasks/CMakeLists.txt +++ b/Analysis/Tasks/CMakeLists.txt @@ -17,7 +17,6 @@ add_subdirectory(PWGUD) add_subdirectory(Utils) add_subdirectory(SkimmingTutorials) add_subdirectory(PID) -add_subdirectory(EventFiltering) o2_add_dpl_workflow(trackextension SOURCES trackextension.cxx From fd958311dc1c59d28fcae6e36d6b18083fccfc3a Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 20 Apr 2021 11:29:35 +0200 Subject: [PATCH 15/18] Clang-format the new trigger files --- .../centralEventFilterProcessor.cxx | 68 +++++++++---------- .../centralEventFilterProcessor.h | 8 +-- Analysis/EventFiltering/filterTables.h | 4 +- Analysis/EventFiltering/nucleiFilter.cxx | 52 +++++++------- 4 files changed, 65 insertions(+), 67 deletions(-) diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.cxx b/Analysis/EventFiltering/centralEventFilterProcessor.cxx index 88ec171d632bb..822734a41acb3 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.cxx +++ b/Analysis/EventFiltering/centralEventFilterProcessor.cxx @@ -28,37 +28,39 @@ using namespace o2::framework; using namespace rapidjson; -namespace { - Document readJsonFile(std::string& config) { - FILE* fp = fopen(config.data(), "rb"); +namespace +{ +Document readJsonFile(std::string& config) +{ + FILE* fp = fopen(config.data(), "rb"); - char readBuffer[65536]; - FileReadStream is(fp, readBuffer, sizeof(readBuffer)); + char readBuffer[65536]; + FileReadStream is(fp, readBuffer, sizeof(readBuffer)); - Document d; - d.ParseStream(is); - fclose(fp); - return d; - } + Document d; + d.ParseStream(is); + fclose(fp); + return d; } +} // namespace namespace o2::aod::filtering { void CentralEventFilterProcessor::init(framework::InitContext& ic) { - // JSON example - // { - // "subwagon_name" : "CentralEventFilterProcessor", - // "configuration" : { - // "NucleiFilters" : { - // "H2" : 0.1, - // "H3" : 0.3, - // "HE3" : 1., - // "HE4" : 1. - // } - // } - // } + // JSON example + // { + // "subwagon_name" : "CentralEventFilterProcessor", + // "configuration" : { + // "NucleiFilters" : { + // "H2" : 0.1, + // "H3" : 0.3, + // "HE3" : 1., + // "HE4" : 1. + // } + // } + // } std::cout << "Start init" << std::endl; Document d = readJsonFile(mConfigFile); int nCols{0}; @@ -80,13 +82,12 @@ void CentralEventFilterProcessor::init(framework::InitContext& ic) } } std::cout << "Middle init" << std::endl; - mScalers = new TH1D("mScalers",";;Number of events", nCols + 1, -0.5, 0.5 + nCols); + mScalers = new TH1D("mScalers", ";;Number of events", nCols + 1, -0.5, 0.5 + nCols); mScalers->GetXaxis()->SetBinLabel(1, "Total number of events"); - mFiltered = new TH1D("mFiltered",";;Number of filtered events", nCols + 1, -0.5, 0.5 + nCols); + mFiltered = new TH1D("mFiltered", ";;Number of filtered events", nCols + 1, -0.5, 0.5 + nCols); mFiltered->GetXaxis()->SetBinLabel(1, "Total number of events"); - int bin{2}; for (auto& table : mDownscaling) { for (auto& column : table.second) { @@ -94,8 +95,8 @@ void CentralEventFilterProcessor::init(framework::InitContext& ic) mFiltered->GetXaxis()->SetBinLabel(bin++, column.first.data()); } } - - TFile test("test.root","recreate"); + + TFile test("test.root", "recreate"); mScalers->Clone()->Write(); test.Close(); } @@ -119,7 +120,7 @@ void CentralEventFilterProcessor::run(ProcessingContext& pc) double binCenter{mScalers->GetXaxis()->GetBinCenter(bin)}; auto column{tablePtr->GetColumnByName(colName.first)}; double downscaling{colName.second}; - if (column) { + if (column) { for (int64_t iC{0}; iC < column->num_chunks(); ++iC) { auto chunk{column->chunk(iC)}; auto boolArray = std::static_pointer_cast(chunk); @@ -137,16 +138,15 @@ void CentralEventFilterProcessor::run(ProcessingContext& pc) } mScalers->SetBinContent(1, mScalers->GetBinContent(1) + nEvents); mFiltered->SetBinContent(1, mFiltered->GetBinContent(1) + nEvents); - } void CentralEventFilterProcessor::endOfStream(EndOfStreamContext& ec) { - TFile output("trigger.root","recreate"); + TFile output("trigger.root", "recreate"); mScalers->Write("Scalers"); mFiltered->Write("FilteredScalers"); - mScalers->Scale(1./mScalers->GetBinContent(1)); - mFiltered->Scale(1./mFiltered->GetBinContent(1)); + mScalers->Scale(1. / mScalers->GetBinContent(1)); + mFiltered->Scale(1. / mFiltered->GetBinContent(1)); mScalers->Write("Fractions"); mFiltered->Write("FractionsDownscaled"); output.Close(); @@ -157,7 +157,7 @@ DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config) std::vector inputs; Document d = readJsonFile(config); - + for (auto& workflow : d["workflows"].GetArray()) { for (unsigned int iFilter{0}; iFilter < AvailableFilters.size(); ++iFilter) { if (std::string_view(workflow["subwagon_name"].GetString()) == std::string_view(AvailableFilters[iFilter])) { @@ -178,7 +178,7 @@ DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config) AlgorithmSpec{adaptFromTask(config)}, Options{ {"filtering-config", VariantType::String, "", {"Path to the filtering json config file"}}}}; -} +} } // namespace o2::aod::filtering diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.h b/Analysis/EventFiltering/centralEventFilterProcessor.h index 2381b5c549239..ad2e671902099 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.h +++ b/Analysis/EventFiltering/centralEventFilterProcessor.h @@ -27,7 +27,7 @@ namespace o2::aod::filtering class CentralEventFilterProcessor : public framework::Task { public: - CentralEventFilterProcessor(const std::string &config) : mConfigFile{config} {} + CentralEventFilterProcessor(const std::string& config) : mConfigFile{config} {} ~CentralEventFilterProcessor() override = default; void init(framework::InitContext& ic) final; void run(framework::ProcessingContext& pc) final; @@ -37,13 +37,13 @@ class CentralEventFilterProcessor : public framework::Task TH1D* mScalers; TH1D* mFiltered; std::string mConfigFile; - std::unordered_map> mDownscaling; + std::unordered_map> mDownscaling; std::mt19937_64 mGeneratorEngine; - std::uniform_real_distribution mUniformGenerator = std::uniform_real_distribution(0.,1.); + std::uniform_real_distribution mUniformGenerator = std::uniform_real_distribution(0., 1.); }; /// create a processor spec -framework::DataProcessorSpec getCentralEventFilterProcessorSpec(std::string &config); +framework::DataProcessorSpec getCentralEventFilterProcessorSpec(std::string& config); } // namespace o2::aod::filtering diff --git a/Analysis/EventFiltering/filterTables.h b/Analysis/EventFiltering/filterTables.h index 573e135b12e64..6868b69990e73 100644 --- a/Analysis/EventFiltering/filterTables.h +++ b/Analysis/EventFiltering/filterTables.h @@ -26,8 +26,8 @@ DECLARE_SOA_COLUMN(He4, hasHe4, bool); DECLARE_SOA_TABLE(NucleiFilters, "AOD", "Nuclei Filters", filtering::H2, filtering::H3, filtering::He3, filtering::He4); -constexpr std::array AvailableFilters{"NucleiFilters"}; -constexpr std::array FilterDescriptions{"Nuclei Filters"}; +constexpr std::array AvailableFilters{"NucleiFilters"}; +constexpr std::array FilterDescriptions{"Nuclei Filters"}; using NucleiFilter = NucleiFilters::iterator; } // namespace o2::aod diff --git a/Analysis/EventFiltering/nucleiFilter.cxx b/Analysis/EventFiltering/nucleiFilter.cxx index 5e97a29d6b4b6..c0e89e7e09848 100644 --- a/Analysis/EventFiltering/nucleiFilter.cxx +++ b/Analysis/EventFiltering/nucleiFilter.cxx @@ -29,30 +29,30 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -namespace { - float rapidity(float pt, float eta, float m) { - return std::asinh(pt / std::hypot(m, pt) * std::sinh(eta)); - } - - static constexpr int nNuclei{4}; - static constexpr int nCutsPID{5}; - static constexpr std::array masses{ - constants::physics::MassDeuteron, constants::physics::MassTriton, - constants::physics::MassHelium3, constants::physics::MassAlpha - }; - static constexpr std::array charges{1, 1, 2, 2}; - static const std::vector nucleiNames{"H2", "H3", "He3", "He4"}; - static const std::vector cutsNames{ - "TPCnSigmaMin", "TPCnSigmaMax", "TOFnSigmaMin", "TOFnSigmaMax", "TOFpidStartPt" - }; - static constexpr float cutsPID[nNuclei][nCutsPID]{ - {-3.f, +3.f, -4.f, +4.f, 1.0f}, /*H2*/ - {-3.f, +3.f, -4.f, +4.f, 1.6f}, /*H3*/ - {-5.f, +5.f, -4.f, +4.f, 14000.f}, /*He3*/ - {-5.f, +5.f, -4.f, +4.f, 14000.f} /*He4*/ - }; +namespace +{ +float rapidity(float pt, float eta, float m) +{ + return std::asinh(pt / std::hypot(m, pt) * std::sinh(eta)); } +static constexpr int nNuclei{4}; +static constexpr int nCutsPID{5}; +static constexpr std::array masses{ + constants::physics::MassDeuteron, constants::physics::MassTriton, + constants::physics::MassHelium3, constants::physics::MassAlpha}; +static constexpr std::array charges{1, 1, 2, 2}; +static const std::vector nucleiNames{"H2", "H3", "He3", "He4"}; +static const std::vector cutsNames{ + "TPCnSigmaMin", "TPCnSigmaMax", "TOFnSigmaMin", "TOFnSigmaMax", "TOFpidStartPt"}; +static constexpr float cutsPID[nNuclei][nCutsPID]{ + {-3.f, +3.f, -4.f, +4.f, 1.0f}, /*H2*/ + {-3.f, +3.f, -4.f, +4.f, 1.6f}, /*H3*/ + {-5.f, +5.f, -4.f, +4.f, 14000.f}, /*He3*/ + {-5.f, +5.f, -4.f, +4.f, 14000.f} /*He4*/ +}; +} // namespace + struct nucleiFilter { Produces tags; @@ -79,7 +79,7 @@ struct nucleiFilter { spectra.add("fCollZpos", "collision z position", HistType::kTH1F, {{600, -20., +20., "z position (cm)"}}); spectra.add("fTPCsignal", "Specific energy loss", HistType::kTH2F, {{600, 0., 3, "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); spectra.add("fTPCcounts", "n-sigma TPC", HistType::kTH2F, {ptAxis, {200, -100., +100., "n#sigma_{He} (a. u.)"}}); - spectra.add("fProcessedEvents", "Nuclei - event filtered", HistType::kTH1F,{{4 ,-0.5, 3.5, "Event counter"}}); + spectra.add("fProcessedEvents", "Nuclei - event filtered", HistType::kTH1F, {{4, -0.5, 3.5, "Event counter"}}); } Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; @@ -97,11 +97,9 @@ struct nucleiFilter { for (auto track : tracks) { // start loop over tracks const float nSigmaTPC[nNuclei]{ - track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl()} - ; + track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl()}; const float nSigmaTOF[nNuclei]{ - track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe(), track.tofNSigmaAl() - }; + track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe(), track.tofNSigmaAl()}; for (int iN{0}; iN < nNuclei; ++iN) { float y{rapidity(track.pt() * charges[iN], track.eta(), masses[iN])}; From 400e46fe860b1fcb0fef6099383de1092ee88bf8 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Fri, 7 May 2021 16:23:13 +0200 Subject: [PATCH 16/18] Fix first round of Jan's comments Co-authored-by: Stefano Trogolo --- Analysis/EventFiltering/centralEventFilterProcessor.cxx | 8 ++++++-- Analysis/EventFiltering/nucleiFilter.cxx | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Analysis/EventFiltering/centralEventFilterProcessor.cxx b/Analysis/EventFiltering/centralEventFilterProcessor.cxx index 822734a41acb3..867bbc4a1ccfa 100644 --- a/Analysis/EventFiltering/centralEventFilterProcessor.cxx +++ b/Analysis/EventFiltering/centralEventFilterProcessor.cxx @@ -145,8 +145,12 @@ void CentralEventFilterProcessor::endOfStream(EndOfStreamContext& ec) TFile output("trigger.root", "recreate"); mScalers->Write("Scalers"); mFiltered->Write("FilteredScalers"); - mScalers->Scale(1. / mScalers->GetBinContent(1)); - mFiltered->Scale(1. / mFiltered->GetBinContent(1)); + if (mScalers->GetBinContent(1) > 1.e-24) { + mScalers->Scale(1. / mScalers->GetBinContent(1)); + } + if (mFiltered->GetBinContent(1) > 1.e-24) { + mFiltered->Scale(1. / mFiltered->GetBinContent(1)); + } mScalers->Write("Fractions"); mFiltered->Write("FractionsDownscaled"); output.Close(); diff --git a/Analysis/EventFiltering/nucleiFilter.cxx b/Analysis/EventFiltering/nucleiFilter.cxx index c0e89e7e09848..ec9c4e8560ad3 100644 --- a/Analysis/EventFiltering/nucleiFilter.cxx +++ b/Analysis/EventFiltering/nucleiFilter.cxx @@ -89,7 +89,7 @@ struct nucleiFilter { void process(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, TrackCandidates const& tracks) { // collision process loop - bool keepEvent[nNuclei]{false, false, false, false}; + bool keepEvent[nNuclei]{false}; // spectra.fill(HIST("fCollZpos"), collision.posZ()); // From 5cf98c8a4ba3fea81133b1bf8401c7e4aa513e89 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 18 May 2021 11:00:10 +0200 Subject: [PATCH 17/18] More meaningful name for the executable --- Analysis/EventFiltering/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Analysis/EventFiltering/CMakeLists.txt b/Analysis/EventFiltering/CMakeLists.txt index 12a5bee37c2c3..d23b2879fae2b 100644 --- a/Analysis/EventFiltering/CMakeLists.txt +++ b/Analysis/EventFiltering/CMakeLists.txt @@ -13,7 +13,7 @@ o2_add_library( SOURCES centralEventFilterProcessor.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::AnalysisDataModel O2::AnalysisCore) -o2_add_executable(cefp +o2_add_executable(central-event-filter-processor SOURCES cefp.cxx COMPONENT_NAME Analysis PUBLIC_LINK_LIBRARIES O2::EventFiltering) From 42e1969e078d5b30d20f9f23d15f9aa1c2eb02f5 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 18 May 2021 12:00:56 +0200 Subject: [PATCH 18/18] Fix for latest PID enum names --- Analysis/EventFiltering/nucleiFilter.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Analysis/EventFiltering/nucleiFilter.cxx b/Analysis/EventFiltering/nucleiFilter.cxx index ec9c4e8560ad3..30620808d9879 100644 --- a/Analysis/EventFiltering/nucleiFilter.cxx +++ b/Analysis/EventFiltering/nucleiFilter.cxx @@ -85,7 +85,7 @@ struct nucleiFilter { Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::isGlobalTrack == static_cast(1u)); - using TrackCandidates = soa::Filtered>; + using TrackCandidates = soa::Filtered>; void process(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, TrackCandidates const& tracks) { // collision process loop