diff --git a/PWGCF/CMakeLists.txt b/PWGCF/CMakeLists.txt
index f31b23ad6a3..23afeaf6d0d 100644
--- a/PWGCF/CMakeLists.txt
+++ b/PWGCF/CMakeLists.txt
@@ -17,4 +17,5 @@ add_subdirectory(FemtoWorld)
add_subdirectory(MultiparticleCorrelations)
add_subdirectory(Tasks)
add_subdirectory(TableProducer)
+add_subdirectory(Tutorial)
diff --git a/PWGCF/Tutorial/CFTutorialTask0.cxx b/PWGCF/Tutorial/CFTutorialTask0.cxx
new file mode 100644
index 00000000000..3de52379610
--- /dev/null
+++ b/PWGCF/Tutorial/CFTutorialTask0.cxx
@@ -0,0 +1,68 @@
+// 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.
+
+/// \author Luca Barioglio
+
+// O2 includes
+#include "Framework/AnalysisTask.h"
+#include "Framework/runDataProcessing.h"
+
+using namespace o2;
+using namespace o2::framework;
+using namespace o2::framework::expressions;
+
+// STEP 0
+// Example task illustrating how to create histograms and fill them with basic information.
+// A basic event selection is applied.
+
+struct CFTutorialTask0 {
+ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject};
+
+ // Equivalent of the AliRoot task UserCreateOutputObjects
+ void init(o2::framework::InitContext&)
+ {
+ // Define your axes
+ // Constant bin width axis
+ AxisSpec vtxZAxis = {100, -20, 20};
+ // Variable bin width axis
+ 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.};
+ AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"};
+
+ // Add histograms to histogram manager (as in the output object of in AliPhysics)
+ histos.add("hZvtx_before_sel", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hZvtx_after_sel", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hP", ";#it{p} (GeV/#it{c})", kTH1F, {{35, 0.5, 4.}});
+ histos.add("hPt", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis});
+ }
+
+ // Equivalent of the AliRoot task UserExec
+ void process(aod::Collision const& coll, aod::Tracks const& inputTracks)
+ {
+ // Performing the event selection
+ histos.fill(HIST("hZvtx_before_sel"), coll.posZ());
+ if (fabs(coll.posZ()) > 10.f) {
+ return;
+ }
+ histos.fill(HIST("hZvtx_after_sel"), coll.posZ());
+
+ for (auto track : inputTracks) { // Loop over tracks
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ }
+ }
+};
+
+WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
+{
+ // Equivalent to the AddTask in AliPhysics
+ WorkflowSpec workflow{adaptAnalysisTask(cfgc)};
+ return workflow;
+}
diff --git a/PWGCF/Tutorial/CFTutorialTask1.cxx b/PWGCF/Tutorial/CFTutorialTask1.cxx
new file mode 100644
index 00000000000..f79eb1871c2
--- /dev/null
+++ b/PWGCF/Tutorial/CFTutorialTask1.cxx
@@ -0,0 +1,88 @@
+// 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.
+
+/// \author Luca Barioglio
+
+// O2 includes
+#include "Framework/AnalysisTask.h"
+#include "Framework/runDataProcessing.h"
+#include "Common/DataModel/EventSelection.h"
+#include "Common/DataModel/Multiplicity.h"
+#include "Common/DataModel/PIDResponse.h"
+
+using namespace o2;
+using namespace o2::framework;
+using namespace o2::framework::expressions;
+
+// STEP 1
+// Example task illustrating how to acess information from different tables
+
+namespace o2::aod
+{
+using MyCollisions = soa::Join;
+using MyTracks = soa::Join;
+using MyCollision = MyCollisions::iterator;
+using MyTrack = MyTracks::iterator;
+} // namespace o2::aod
+
+struct CFTutorialTask1 {
+ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject};
+
+ // Equivalent of the AliRoot task UserCreateOutputObjects
+ void init(o2::framework::InitContext&)
+ {
+ // Define your axes
+ // Constant bin width axis
+ AxisSpec vtxZAxis = {100, -20, 20};
+ // Variable bin width axis
+ 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.};
+ AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"};
+
+ // Add histograms to histogram manager (as in the output object of in AliPhysics)
+ histos.add("hZvtx_before_sel", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hZvtx_after_sel", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hP", ";#it{p} (GeV/#it{c})", kTH1F, {{35, 0.5, 4.}});
+ histos.add("hEta", ";#it{p} (GeV/#it{c})", kTH1F, {{100, -1.5, 1.5}});
+ histos.add("hPt", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis});
+ histos.add("hNsigmaTPCP", ";#it{p} (GeV/#it{c}); n#sigma_{TPC}^{p}", kTH2F, {{35, 0.5, 4.}, {100, -5., 5.}});
+ }
+
+ // Equivalent of the AliRoot task UserExec
+ void process(aod::MyCollision const& coll, aod::MyTracks const& inputTracks)
+ {
+ // Performing the event selection
+ histos.fill(HIST("hZvtx_before_sel"), coll.posZ());
+ if (fabs(coll.posZ()) > 10.f) {
+ return;
+ }
+ histos.fill(HIST("hZvtx_after_sel"), coll.posZ());
+
+ for (auto track : inputTracks) { // Loop over tracks
+ if (fabs(track.eta()) > 0.8) {
+ continue;
+ }
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPr());
+ }
+ }
+};
+
+WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
+{
+ // Equivalent to the AddTask in AliPhysics
+ WorkflowSpec workflow{adaptAnalysisTask(cfgc)};
+ return workflow;
+}
diff --git a/PWGCF/Tutorial/CFTutorialTask2.cxx b/PWGCF/Tutorial/CFTutorialTask2.cxx
new file mode 100644
index 00000000000..bf40ce17353
--- /dev/null
+++ b/PWGCF/Tutorial/CFTutorialTask2.cxx
@@ -0,0 +1,100 @@
+// 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.
+
+/// \author Luca Barioglio
+
+// O2 includes
+#include "Framework/AnalysisTask.h"
+#include "Framework/runDataProcessing.h"
+#include "Common/DataModel/EventSelection.h"
+#include "Common/DataModel/Multiplicity.h"
+#include "Common/DataModel/PIDResponse.h"
+
+using namespace o2;
+using namespace o2::framework;
+using namespace o2::framework::expressions;
+
+// STEP 2
+// Example task illustrating how to use Configurables and Filters
+
+namespace o2::aod
+{
+using MyCollisions = soa::Join;
+using MyTracks = soa::Join;
+using MyCollision = MyCollisions::iterator;
+using MyTrack = MyTracks::iterator;
+} // namespace o2::aod
+
+struct CFTutorialTask2 {
+ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject};
+
+ // Defining configurables
+ Configurable ConfZvtxCut{"ConfZvtxCut", 10, "Z vtx cut"};
+ Configurable ConfEtaCut{"ConfEtaCut", 0.8, "Pseudorapidity cut"};
+ Configurable ConfMaxPtCut{"ConfMaxPtCut", 3.0, "Max Pt cut"};
+ Configurable ConfMinPtCut{"ConfMinPtCut", 0.5, "Min Pt cut"};
+ Configurable ConfMinNSigmaTPCCut{"ConfMinNSigmaTPCCut", 3, "N-sigma TPC cut"};
+
+ // Defining filters
+ Filter collisionFilter = (nabs(aod::collision::posZ) < ConfZvtxCut);
+ Filter trackFilter = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut);
+
+ // Applying filters
+ using MyFilteredCollisions = soa::Filtered;
+ using MyFilteredCollision = MyFilteredCollisions::iterator;
+ using MyFilteredTracks = soa::Filtered;
+ using MyFilteredTrack = MyFilteredTracks::iterator;
+
+ // Equivalent of the AliRoot task UserCreateOutputObjects
+ void init(o2::framework::InitContext&)
+ {
+ // Define your axes
+ // Constant bin width axis
+ AxisSpec vtxZAxis = {100, -20, 20};
+ // Variable bin width axis
+ 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.};
+ AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"};
+
+ // Add histograms to histogram manager (as in the output object of in AliPhysics)
+ histos.add("hZvtx", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hP", ";#it{p} (GeV/#it{c})", kTH1F, {{35, 0.5, 4.}});
+ histos.add("hEta", ";#it{p} (GeV/#it{c})", kTH1F, {{100, -1.5, 1.5}});
+ histos.add("hPt", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis});
+ histos.add("hNsigmaTPCP", ";#it{p} (GeV/#it{c}); n#sigma_{TPC}^{p}", kTH2F, {{35, 0.5, 4.}, {100, -5., 5.}});
+ }
+
+ // Equivalent of the AliRoot task UserExec
+ void process(MyFilteredCollision const& coll, MyFilteredTracks const& inputTracks)
+ {
+ histos.fill(HIST("hZvtx"), coll.posZ());
+
+ for (auto track : inputTracks) {
+ if (fabs(track.tpcNSigmaPr()) > ConfMinNSigmaTPCCut) { // TPCNSigmaPr is a dynamic column and it is not compatible with Filters
+ continue;
+ }
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hEta"), track.eta());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPr());
+ }
+ }
+};
+
+WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
+{
+ // Equivalent to the AddTask in AliPhysics
+ WorkflowSpec workflow{adaptAnalysisTask(cfgc)};
+ return workflow;
+}
diff --git a/PWGCF/Tutorial/CFTutorialTask3.cxx b/PWGCF/Tutorial/CFTutorialTask3.cxx
new file mode 100644
index 00000000000..3735facf3d2
--- /dev/null
+++ b/PWGCF/Tutorial/CFTutorialTask3.cxx
@@ -0,0 +1,112 @@
+// 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.
+
+/// \author Luca Barioglio
+
+// O2 includes
+#include "Framework/AnalysisTask.h"
+#include "Framework/runDataProcessing.h"
+#include "Common/DataModel/EventSelection.h"
+#include "Common/DataModel/Multiplicity.h"
+#include "Common/DataModel/PIDResponse.h"
+
+using namespace o2;
+using namespace o2::framework;
+using namespace o2::framework::expressions;
+
+// STEP 2
+// Example task illustrating how to create and use partitions
+
+namespace o2::aod
+{
+using MyCollisions = soa::Join;
+using MyTracks = soa::Join;
+using MyCollision = MyCollisions::iterator;
+using MyTrack = MyTracks::iterator;
+} // namespace o2::aod
+
+struct CFTutorialTask3 {
+ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject};
+
+ // Defining configurables
+ Configurable ConfZvtxCut{"ConfZvtxCut", 10, "Z vtx cut"};
+ Configurable ConfEtaCut{"ConfEtaCut", 0.8, "Pseudorapidity cut"};
+ Configurable ConfMaxPtCut{"ConfMaxPtCut", 3.0, "Max Pt cut"};
+ Configurable ConfMinPtCut{"ConfMinPtCut", 0.5, "Min Pt cut"};
+ Configurable ConfMinNSigmaTPCCut{"ConfMinNSigmaTPCCut", 3., "N-sigma TPC cut"};
+ Configurable ConfChargeCut{"ConfChargeCut", 0., "N-sigma TPC cut"};
+
+ // Defining filters
+ Filter collisionFilter = (nabs(aod::collision::posZ) < ConfZvtxCut);
+ Filter trackFilter = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut);
+
+ // Applying filters
+ using MyFilteredCollisions = soa::Filtered;
+ using MyFilteredCollision = MyFilteredCollisions::iterator;
+
+ Partition positive = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut) && (aod::track::signed1Pt > ConfChargeCut);
+ Partition negative = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut) && (aod::track::signed1Pt < ConfChargeCut);
+
+ // Equivalent of the AliRoot task UserCreateOutputObjects
+ void init(o2::framework::InitContext&)
+ {
+ // Define your axes
+ // Constant bin width axis
+ AxisSpec vtxZAxis = {100, -20, 20};
+ // Variable bin width axis
+ 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.};
+ AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"};
+
+ // Add histograms to histogram manager (as in the output object of in AliPhysics)
+ histos.add("hZvtx", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hP", ";#it{p} (GeV/#it{c})", kTH1F, {{35, 0.5, 4.}});
+ histos.add("hEta", ";#it{p} (GeV/#it{c})", kTH1F, {{100, -1.5, 1.5}});
+ histos.add("hPt", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis});
+ histos.add("hNsigmaTPCP", ";#it{p} (GeV/#it{c}); n#sigma_{TPC}^{#pi}", kTH2F, {{35, 0.5, 4.}, {100, -5., 5.}});
+ histos.add("hChargePos", ";z;", kTH1F, {{3, -1.5, 1.5}});
+ histos.add("hChargeNeg", ";z;", kTH1F, {{3, -1.5, 1.5}});
+ }
+
+ // Equivalent of the AliRoot task UserExec
+ void process(MyFilteredCollision const& coll, o2::aod::MyTracks const& tracks)
+ {
+ auto groupPositive = positive->sliceByCached(aod::track::collisionId, coll.globalIndex());
+ auto groupNegative = negative->sliceByCached(aod::track::collisionId, coll.globalIndex());
+ histos.fill(HIST("hZvtx"), coll.posZ());
+
+ for (auto track : groupPositive) {
+ histos.fill(HIST("hChargePos"), track.sign());
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hEta"), track.eta());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi());
+ }
+
+ for (auto track : groupNegative) {
+ histos.fill(HIST("hChargeNeg"), track.sign());
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hEta"), track.eta());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi());
+ }
+ }
+};
+
+WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
+{
+ // Equivalent to the AddTask in AliPhysics
+ WorkflowSpec workflow{adaptAnalysisTask(cfgc)};
+ return workflow;
+}
diff --git a/PWGCF/Tutorial/CFTutorialTask4.cxx b/PWGCF/Tutorial/CFTutorialTask4.cxx
new file mode 100644
index 00000000000..64f8a551f10
--- /dev/null
+++ b/PWGCF/Tutorial/CFTutorialTask4.cxx
@@ -0,0 +1,130 @@
+// 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.
+
+/// \author Luca Barioglio
+
+// O2 includes
+#include "Framework/AnalysisTask.h"
+#include "Framework/runDataProcessing.h"
+#include "Common/DataModel/EventSelection.h"
+#include "Common/DataModel/Multiplicity.h"
+#include "Common/DataModel/PIDResponse.h"
+#include "CommonConstants/PhysicsConstants.h"
+
+#include "TLorentzVector.h"
+
+using namespace o2;
+using namespace o2::framework;
+using namespace o2::framework::expressions;
+
+// STEP 2
+// Example task illustrating how to mix elements of different partitions
+
+namespace o2::aod
+{
+using MyCollisions = soa::Join;
+using MyTracks = soa::Join;
+using MyCollision = MyCollisions::iterator;
+using MyTrack = MyTracks::iterator;
+} // namespace o2::aod
+
+struct CFTutorialTask4 {
+ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject};
+
+ // Defining configurables
+ Configurable ConfZvtxCut{"ConfZvtxCut", 10, "Z vtx cut"};
+ Configurable ConfEtaCut{"ConfEtaCut", 0.8, "Pseudorapidity cut"};
+ Configurable ConfMaxPtCut{"ConfMaxPtCut", 3.0, "Max Pt cut"};
+ Configurable ConfMinPtCut{"ConfMinPtCut", 0.5, "Min Pt cut"};
+ Configurable ConfMinNSigmaTPCCut{"ConfMinNSigmaTPCCut", 3., "N-sigma TPC cut"};
+ Configurable ConfChargeCut{"ConfChargeCut", 0., "N-sigma TPC cut"};
+
+ // Defining filters
+ Filter collisionFilter = (nabs(aod::collision::posZ) < ConfZvtxCut);
+ Filter trackFilter = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut);
+
+ // Applying filters
+ using MyFilteredCollisions = soa::Filtered;
+ using MyFilteredCollision = MyFilteredCollisions::iterator;
+
+ Partition positive = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut) && (aod::track::signed1Pt > ConfChargeCut);
+ Partition negative = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut) && (aod::track::signed1Pt < ConfChargeCut);
+
+ // Equivalent of the AliRoot task UserCreateOutputObjects
+ void init(o2::framework::InitContext&)
+ {
+ // Define your axes
+ // Constant bin width axis
+ AxisSpec vtxZAxis = {100, -20, 20};
+ // Variable bin width axis
+ 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.};
+ AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"};
+
+ // Add histograms to histogram manager (as in the output object of in AliPhysics)
+ histos.add("hZvtx", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hP", ";#it{p} (GeV/#it{c})", kTH1F, {{35, 0.5, 4.}});
+ histos.add("hEta", ";#it{p} (GeV/#it{c})", kTH1F, {{100, -1.5, 1.5}});
+ histos.add("hPt", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis});
+ histos.add("hNsigmaTPCP", ";#it{p} (GeV/#it{c}); n#sigma_{TPC}^{#pi}", kTH2F, {{35, 0.5, 4.}, {100, -5., 5.}});
+ histos.add("hChargePos", ";z;", kTH1F, {{3, -1.5, 1.5}});
+ histos.add("hChargeNeg", ";z;", kTH1F, {{3, -1.5, 1.5}});
+ histos.add("hInvariantMass", ";M_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2});", kTH1F, {{100, 0., 1.0}});
+ }
+
+ // Equivalent of the AliRoot task UserExec
+ void process(MyFilteredCollision const& coll, o2::aod::MyTracks const& tracks)
+ {
+ auto groupPositive = positive->sliceByCached(aod::track::collisionId, coll.globalIndex());
+ auto groupNegative = negative->sliceByCached(aod::track::collisionId, coll.globalIndex());
+ histos.fill(HIST("hZvtx"), coll.posZ());
+
+ for (auto track : groupPositive) {
+ histos.fill(HIST("hChargePos"), track.sign());
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hEta"), track.eta());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi());
+ }
+
+ for (auto track : groupNegative) {
+ histos.fill(HIST("hChargeNeg"), track.sign());
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hEta"), track.eta());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi());
+ }
+
+ for (auto& [pos, neg] : combinations(soa::CombinationsFullIndexPolicy(groupPositive, groupNegative))) {
+ if (fabs(pos.tpcNSigmaPi()) > 3 or fabs(neg.tpcNSigmaPi()) > 3) {
+ continue;
+ }
+ TLorentzVector posVec;
+ posVec.SetPtEtaPhiM(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassPionCharged);
+ TLorentzVector negVec;
+ negVec.SetPtEtaPhiM(neg.pt(), neg.eta(), neg.phi(), o2::constants::physics::MassPionCharged);
+
+ TLorentzVector sumVec(posVec);
+ sumVec += negVec;
+ histos.fill(HIST("hInvariantMass"), sumVec.M());
+ }
+ }
+};
+
+WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
+{
+ // Equivalent to the AddTask in AliPhysics
+ WorkflowSpec workflow{adaptAnalysisTask(cfgc)};
+ return workflow;
+}
diff --git a/PWGCF/Tutorial/CFTutorialTask5.cxx b/PWGCF/Tutorial/CFTutorialTask5.cxx
new file mode 100644
index 00000000000..a097786f953
--- /dev/null
+++ b/PWGCF/Tutorial/CFTutorialTask5.cxx
@@ -0,0 +1,190 @@
+// 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.
+
+/// \author Luca Barioglio
+
+// O2 includes
+#include "Framework/AnalysisTask.h"
+#include "Framework/runDataProcessing.h"
+#include "Common/DataModel/EventSelection.h"
+#include "Common/DataModel/Multiplicity.h"
+#include "Common/DataModel/PIDResponse.h"
+#include "CommonConstants/PhysicsConstants.h"
+
+#include "TLorentzVector.h"
+
+using namespace o2;
+using namespace o2::framework;
+using namespace o2::framework::expressions;
+
+// STEP 2
+// Example task illustrating how to mix elements of different partitions and different events + process switches
+
+namespace o2::aod
+{
+using MyCollisions = soa::Join;
+using MyTracks = soa::Join;
+using MyCollision = MyCollisions::iterator;
+using MyTrack = MyTracks::iterator;
+} // namespace o2::aod
+
+struct CFTutorialTask5 {
+ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject};
+
+ // Defining configurables
+ Configurable ConfZvtxCut{"ConfZvtxCut", 10, "Z vtx cut"};
+ Configurable ConfEtaCut{"ConfEtaCut", 0.8, "Pseudorapidity cut"};
+ Configurable ConfMaxPtCut{"ConfMaxPtCut", 3.0, "Max Pt cut"};
+ Configurable ConfMinPtCut{"ConfMinPtCut", 0.5, "Min Pt cut"};
+ Configurable ConfMinNSigmaTPCCut{"ConfMinNSigmaTPCCut", 3., "N-sigma TPC cut"};
+ Configurable ConfChargeCut{"ConfChargeCut", 0., "N-sigma TPC cut"};
+
+ // Defining filters
+ Filter collisionFilter = (nabs(aod::collision::posZ) < ConfZvtxCut);
+ Filter trackFilter = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut);
+
+ // Applying filters
+ using MyFilteredCollisions = soa::Filtered;
+ using MyFilteredCollision = MyFilteredCollisions::iterator;
+ using MyFilteredTracks = soa::Filtered;
+
+ Partition positive = aod::track::signed1Pt > ConfChargeCut;
+ Partition negative = aod::track::signed1Pt < ConfChargeCut;
+
+ ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"};
+ ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"};
+
+ using BinningType = ColumnBinningPolicy;
+
+ // Equivalent of the AliRoot task UserCreateOutputObjects
+ void init(o2::framework::InitContext&)
+ {
+ // Define your axes
+ // Constant bin width axis
+ AxisSpec vtxZAxis = {100, -20, 20};
+ // Variable bin width axis
+ 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.};
+ AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"};
+
+ // Add histograms to histogram manager (as in the output object of in AliPhysics)
+ histos.add("hZvtx", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hP", ";#it{p} (GeV/#it{c})", kTH1F, {{35, 0.5, 4.}});
+ histos.add("hEta", ";#it{p} (GeV/#it{c})", kTH1F, {{100, -1.5, 1.5}});
+ histos.add("hPt", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis});
+ histos.add("hNsigmaTPCP", ";#it{p} (GeV/#it{c}); n#sigma_{TPC}^{#pi}", kTH2F, {{35, 0.5, 4.}, {100, -5., 5.}});
+ histos.add("hChargePos", ";z;", kTH1F, {{3, -1.5, 1.5}});
+ histos.add("hChargeNeg", ";z;", kTH1F, {{3, -1.5, 1.5}});
+ histos.add("hInvariantMass", ";M_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2});", kTH1F, {{100, 0., 1.0}});
+ histos.add("hInvariantMassMixed", ";M_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2});", kTH1F, {{100, 0., 1.0}});
+ histos.add("hInvariantMassMixedInterface", ";M_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2});", kTH1F, {{100, 0., 1.0}});
+ }
+
+ void processSame(MyFilteredCollision const& coll, MyFilteredTracks const& tracks)
+ {
+ auto groupPositive = positive->sliceByCached(aod::track::collisionId, coll.globalIndex());
+ auto groupNegative = negative->sliceByCached(aod::track::collisionId, coll.globalIndex());
+ histos.fill(HIST("hZvtx"), coll.posZ());
+
+ for (auto track : groupPositive) {
+ histos.fill(HIST("hChargePos"), track.sign());
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hEta"), track.eta());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi());
+ }
+
+ for (auto track : groupNegative) {
+ histos.fill(HIST("hChargeNeg"), track.sign());
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ histos.fill(HIST("hEta"), track.eta());
+ histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi());
+ }
+
+ for (auto& [pos, neg] : combinations(soa::CombinationsFullIndexPolicy(groupPositive, groupNegative))) {
+ if (fabs(pos.tpcNSigmaPi()) > 3 or fabs(neg.tpcNSigmaPi()) > 3) {
+ continue;
+ }
+ TLorentzVector posVec;
+ posVec.SetPtEtaPhiM(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassPionCharged);
+ TLorentzVector negVec;
+ negVec.SetPtEtaPhiM(neg.pt(), neg.eta(), neg.phi(), o2::constants::physics::MassPionCharged);
+
+ TLorentzVector sumVec(posVec);
+ sumVec += negVec;
+ histos.fill(HIST("hInvariantMass"), sumVec.M());
+ }
+ }
+ PROCESS_SWITCH(CFTutorialTask5, processSame, "Enable processing same event", true);
+
+ void processMixed(MyFilteredCollisions const& colls, MyFilteredTracks const& tracks)
+ {
+ BinningType colBinning{{ConfVtxBins, ConfMultBins}, true};
+ for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, colls, colls)) {
+ auto groupPositive = positive->sliceByCached(aod::track::collisionId, collision1.globalIndex());
+ auto groupNegative = negative->sliceByCached(aod::track::collisionId, collision2.globalIndex());
+
+ for (auto& [pos, neg] : combinations(soa::CombinationsFullIndexPolicy(groupPositive, groupNegative))) {
+ if (fabs(pos.tpcNSigmaPi()) > 3 or fabs(neg.tpcNSigmaPi()) > 3) {
+ continue;
+ }
+ TLorentzVector posVec;
+ posVec.SetPtEtaPhiM(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassPionCharged);
+ TLorentzVector negVec;
+ negVec.SetPtEtaPhiM(neg.pt(), neg.eta(), neg.phi(), o2::constants::physics::MassPionCharged);
+
+ TLorentzVector sumVec(posVec);
+ sumVec += negVec;
+ histos.fill(HIST("hInvariantMassMixed"), sumVec.M());
+ }
+ }
+ }
+ PROCESS_SWITCH(CFTutorialTask5, processMixed, "Enable processing mixed event", true);
+
+ void processMixedEventInterface(MyFilteredCollisions& colls, MyFilteredTracks& tracks)
+ {
+ auto tracksTuple = std::make_tuple(tracks);
+ BinningType colBinning{{ConfVtxBins, ConfMultBins}, true};
+ SameKindPair pair{colBinning, 5, -1, colls, tracksTuple};
+ for (auto& [c1, tracks1, c2, tracks2] : pair) {
+ Partition groupPositive = aod::track::signed1Pt > ConfChargeCut;
+ groupPositive.bindTable(tracks1);
+ Partition groupNegative = aod::track::signed1Pt < ConfChargeCut;
+ groupNegative.bindTable(tracks2);
+
+ for (auto& [pos, neg] : combinations(soa::CombinationsFullIndexPolicy(groupPositive, groupNegative))) {
+ if (fabs(pos.tpcNSigmaPi()) > 3 or fabs(neg.tpcNSigmaPi()) > 3) {
+ continue;
+ }
+ TLorentzVector posVec;
+ posVec.SetPtEtaPhiM(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassPionCharged);
+ TLorentzVector negVec;
+ negVec.SetPtEtaPhiM(neg.pt(), neg.eta(), neg.phi(), o2::constants::physics::MassPionCharged);
+
+ TLorentzVector sumVec(posVec);
+ sumVec += negVec;
+ histos.fill(HIST("hInvariantMassMixedInterface"), sumVec.M());
+ }
+ }
+ }
+ PROCESS_SWITCH(CFTutorialTask5, processMixedEventInterface, "Enable processing mixed event with standard mixing interface", false);
+};
+
+WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
+{
+ // Equivalent to the AddTask in AliPhysics
+ WorkflowSpec workflow{adaptAnalysisTask(cfgc)};
+ return workflow;
+}
diff --git a/PWGCF/Tutorial/CMakeLists.txt b/PWGCF/Tutorial/CMakeLists.txt
new file mode 100644
index 00000000000..41947b55063
--- /dev/null
+++ b/PWGCF/Tutorial/CMakeLists.txt
@@ -0,0 +1,40 @@
+# 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.
+
+o2physics_add_dpl_workflow(cf-tutorial-0
+ SOURCES CFTutorialTask0.cxx
+ PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore
+ COMPONENT_NAME Analysis)
+
+o2physics_add_dpl_workflow(cf-tutorial-1
+ SOURCES CFTutorialTask1.cxx
+ PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore
+ COMPONENT_NAME Analysis)
+
+o2physics_add_dpl_workflow(cf-tutorial-2
+ SOURCES CFTutorialTask2.cxx
+ PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore
+ COMPONENT_NAME Analysis)
+
+o2physics_add_dpl_workflow(cf-tutorial-3
+ SOURCES CFTutorialTask3.cxx
+ PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore
+ COMPONENT_NAME Analysis)
+
+o2physics_add_dpl_workflow(cf-tutorial-4
+ SOURCES CFTutorialTask4.cxx
+ PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore
+ COMPONENT_NAME Analysis)
+
+o2physics_add_dpl_workflow(cf-tutorial-5
+ SOURCES CFTutorialTask5.cxx
+ PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore
+ COMPONENT_NAME Analysis)
diff --git a/PWGCF/Tutorial/README.md b/PWGCF/Tutorial/README.md
new file mode 100644
index 00000000000..2bbfe85b72f
--- /dev/null
+++ b/PWGCF/Tutorial/README.md
@@ -0,0 +1,372 @@
+# O2Physics Tutorial for CF
+Table of contents:
+1. [Prerequisites](#prerequisites)
+2. [Create your task](#create-your-task-in-o2physics)
+3. [Tutorial checkpoints](#tutorial-checkpoints)
+_________________
+_______________
+# Prerequisites
+## How to install O2Physics Framework?
+1. Build prerequisities for your operating system, configure aliBuild (but don't build packages!) - [here](https://alice-doc.github.io/alice-analysis-tutorial/building/custom.html).
+2. Get a GRID certificate - [here](https://alice-doc.github.io/alice-analysis-tutorial/start/cert.html)
+3. Follow instructions on [ALICE O2 documentation](https://aliceo2group.github.io/analysis-framework/docs/installing/) - Prepare your source code, check prerequisites, build and rebuild and check if everything works!
+
+Keep in mind that it might take quite a long time.
+______________
+## How to update ALICE O2Physics?
+
+To update the software, you need to do the following:
+1. in `alice/alidist`, type `git pull`
+2. in `alice/O2Physics`, type `git pull --rebase`
+3. in `alice/`, type `aliBuild build O2Physics --defaults o2`. Here you can add `--debug` (or `-d`) for more information.
+
+You need to update it frequently, because O2 is constantly evolving. Keep in mind that it might take quite a long time.
+_________
+## How to obtain data files for the analysis?
+
+
+This tutorial is made and tested for pilot-beam data (reconstruction pass4). You can download the reference `AO2D.root` file from [here](https://alimonitor.cern.ch/catalogue/index.jsp?path=%2Falice%2Fdata%2F2021%2FOCT%2F505669%2Fapass4%2FAOD#/alice/data/2021/OCT/505669/apass4/AOD/006). We use file `006`. Be aware that due to the file size (around 400 MB), it could take a while.
+
+
+A lot of Run 2 data converted into the Run 3 format can be found [here](https://alimonitor.cern.ch/trains/train.jsp?train_id=132). A detailed description of how to download converted Run 2 data is reported in the [offifial documentation](https://aliceo2group.github.io/analysis-framework/docs/download/)
+
+In summary, to download a bunch of data, do the following:
+1. scroll all the way down, choose the train number you are interested in and click on run number.
+2. A pop-up window appears: click on the **Test Results**.
+3. Scroll down, find the **Full Train** option and then click on **output**.
+4. Search for the `AO2D.root` file: click on it to download it.
+
+Be aware that it might take a while, because the size is around 1 GB file.
+
+Information about trains (job details) can be found [here](https://alimonitor.cern.ch/job_details.jsp).
+
+____________________________
+____________________________
+# Create your task in O2Physics
+
+There are many examples of simple introductory tasks in O2Physics that can be found in `O2Physics/Tutorials`.
+All the information about how to write a task can be found in the [official documentation](https://aliceo2group.github.io/analysis-framework/docs/tutorials/).
+
+We hope that this hands-on tutorial will help you learning to write your analysis tasks starting from scratch.
+
+_________________
+## A O2Physics task in summary
+
+A task is a C++ `struct`, which must contain a `process` method. The `process` is the equivalent of what in `AliPhysics` was `UserExec`. It is used to access analysis objects, such as collisions, tracks, etc.
+
+In addition, the task can also have an `init` funcion, which is used to initialise analysis-objects such as histogram. It is the equivalent of `UserCreateOutputObjects` in AliPhysics.
+
+In the same source file where the `struct` is defined, one has to add a `defineDataProcessing` method, which is necessary to include the task to the workflow, assigning it a specific name.
+
+The task must be added to the `CMakeLists.txt` file in the same directory. For our tutorial, the first task has name `cf-tutorial-0` and it is defined in the source file `CFTutorialTask0.cxx`. The corresponfing lines in the `CMakeLists.txt` file are:
+
+```
+o2physics_add_dpl_workflow(cf-tutorial-0
+ SOURCES CFTutorialTask0.cxx
+ PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore
+ COMPONENT_NAME Analysis)
+```
+Everything will be (hopefully) clear in the hands-on session.
+
+_____________
+## How to build a task?
+
+The safest way to build your task is to use `aliBuild`, following the same instructions reported in the section [_How to update O2Physics?_](#how-to-update-alice-o2physics)
+
+If you want to save time and opt for a more advance method, read the following section
+
+ **Advanced method**
+
+Go to `~/alice/sw/BUILD/O2Physics-latest-master/O2Physics`.
+If you have properly installed direnv, as soon as you enter a lot of text should appear and it should look like this:
+
+
+
+
+
+
+
+In `~/alice/sw/BUILD/O2Physics-latest-master/O2Physics` enter ninja and O2Physics environment (`alienv load ninja/latest O2Physics/latest`). Then build your task using `ninja stage/bin/`. If you don't know what you should type in `` place, open `CMakeList.txt` and see how your analysis is called there.
+Keep in mind that if you add a new file or modify CMakeList you need to use `cmake .`
+Then, after building part, copy this builded file to directory with AOD file (`cp stage/bin/`). You can skip this step (copying) when you use `ninja install` instead of `ninja` or you can use alibuild.
+____________________
+## How to run your code?
+
+In your directory with the AOD file, do the following:
+1. enter O2Physics envinronment, e.g with `alienv enter O2Physics/latest`
+2. You need to connect to the GRID to acces remote configuration files (CCDB): type `alien.py` or `alien-token-init`. You will be asked for you password.
+3. Run your code as:
+`./o2-analysistutorial-simple-analysis --aod-file [-b]`. Option `-b` stops the GUI from showing (batch mode).
+
+It is very likely that your task has dependencies: you have to run also the corresponding tasks (_helper tasks_).
+For example, for our first task `cf-tutorial-task0` you would need to run:
+
+```
+o2-analysis-timestamp | \
+o2-analysis-track-propagation | \
+o2-analysis-cf-cf-tutorial-0 --aod-file AO2D.root
+```
+
+**Tip**: create a bash script with all the commands that you need!
+
+**Tip**: tasks have parameters that can be provided via command line. For example:
+```
+o2-analysis-mytask --aod-file [-- ]
+```
+When you have many parameters, it is better to provide them in a configuration file in json format:
+```
+o2-analysis-mytask --configuration json://
+```
+In the following, we will use configuration files.
+
+### Possible errors
+Couldn't get TTree.
+
+
+Sometimes you may get an error, for example:
+ ```c
+ [ERROR] Exception caught: Couldn't get TTree "DF_2853960297589372650/O2v0dataext from "
+ ```
+It means that `v0dataext` couldn't be found in your AOD file or it has not been produced by any attached helper task.
+So now you know which table is missing. There are two paths you can choose;
+1. **EASIEST**: follow the instructions reported in [ALICE O2 documentation- Tree not found](https://aliceo2group.github.io/analysis-framework/docs/troubleshooting/treenotfound.html). After entering [ALICE O2 documentation - helper task tables](https://aliceo2group.github.io/analysis-framework/docs/datamodel/helperTaskTables.html), you search the table that you are missing and you will find the task you need to attach as dependency.
+2. More difficult: enter directory `alice/O2Physics` and look for the missing table.
+For example, if you are missing `pidtofka` table, you should type:
+```c
+grep -rnw . -e "PIDTOFKA"
+grep -rnw . -e "PIDTOFKa"
+grep -rnw . -e "pidTOFKa"
+```
+You should see a list of files where this expression occurs: you need to find out the name of the file which produces this table. After you find it, you need to go to the corresponding `CMakeLists.txt` file. There, you will find the name of the task you are looking for. You can see that it has many sections but they all look alike. For example:
+```c
+o2physics_add_dpl_workflow(
+ SOURCES
+ PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore
+ COMPONENT_NAME Analysis)
+```
+So you need to find the **task-name** which will correspond to the **C++ file** you've found using grep ;)
+
+
+
+
+Fatal errors with non-existing CCDB entries.
+
+While trying to run various O2 tasks/tutorials, you can run into fatal errors with non-existing CCDB entries for specific data.
+The reason is often the incorrect configuration of the paramenters. For example, for some executables such as `o2-analysis-timestamp` duging the option `--isRun2MC` could solve the problem. Or, simialary, using the oprion `--isMC` for o2-analysis-event-selection.
+For example:
+
+```c
+o2-analysis-mm-dndeta --aod-file AODmc2.root --aod-memory-rate-limit 100000000000000 -b | \
+o2-analysis-pid-tpc -b | \
+o2-analysis-pid-tof -b | \
+o2-analysis-trackselection -b | \
+o2-analysis-trackextension -b | \
+o2-analysis-event-selection --isMC -b | \
+o2-analysis-timestamp --isRun2MC -b
+```
+
+
+
+
+____________________
+## How to obtain results of the analysis?
+Running command `./o2-analysistutorial-simple-analysis --aod-file -b` (and others of that type) creates .root file and .json file.
+To enter AnalysisResults.root file enter O2Physics environment (`enter O2Physics/latest`) and type: `root -l` and then `new TBrowser`. (`-l` is optional but it runs root without additional information about it).
+You can change histograms manually or you can write macros and run them on .root file. If you've never written any root macros consider [_this repository_](https://github.com/zchochul/KADDre). You can find more on root and how to use it here -> [_root.cern/manual/_](https://root.cern/manual/first_steps_with_root/).
+_____________________________
+_____________________________
+# Tutorial checkpoints
+## First task
+ We will be using [CFTutorialTask0.cxx](https://github.com/CF-tutorials/O2Physics/blob/tutorial/PWGCF/Tutorial/CFTutorialTask0.cxx). It's an example task illustrating how to create histograms and fill them with basic information. We will also apply basic event selection.
+
+ Here you can see a structure of a typical analysis task in the O2:
+ ```c
+ struct myTask{
+ Partition partition_name;
+ Filter filter_name;
+ Configurable name{};
+ void init(){
+ }
+ void process(){
+ }
+ }
+ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc){
+ WorkflowSpec workflow{};
+ return workflow;
+ }
+ ```
+Each part will be described in greater detail further on during this tutorial.
+
+The first element of the task is `init` (equivalent to _UserCreateOutputObjects_ in _AliPhysics_). It looks like this:
+
+ ```c
+ void init(o2::framework::InitContext&)
+ {
+ // Define your axes
+ // Constant bin width axis
+ AxisSpec vtxZAxis = {100, -20, 20};
+ // Variable bin width axis
+ 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.};
+ AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"};
+
+ // Add histograms to histogram manager (as in the output object in AliPhysics)
+ histos.add("hZvtx_before_sel", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hZvtx_after_sel", ";Z (cm)", kTH1F, {vtxZAxis});
+ histos.add("hP", ";#it{p} (GeV/#it{c})", kTH1F, {{35, 0.5, 4.}});
+ histos.add("hPt", ";#it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis});
+ }
+ ```
+`init` is used to initialise objects, e.g. histograms, which will be used later in the process.
+
+The second element is `process`, equivalent to the _UserExec_ in _AliPhysics_. It looks like this:
+
+```c
+ void process(aod::Collision const& coll, aod::Tracks const& inputTracks)
+ {
+ // Performing the event selection
+ histos.fill(HIST("hZvtx_before_sel"), coll.posZ());
+ if (fabs(coll.posZ()) > 10.f) {
+ return;
+ }
+ histos.fill(HIST("hZvtx_after_sel"), coll.posZ());
+
+ for (auto track : inputTracks) { // Loop over tracks
+ histos.fill(HIST("hP"), track.p());
+ histos.fill(HIST("hPt"), track.pt());
+ }
+ }
+ ```
+
+The arguments of `process` are the data _tables_ that we analyse, i.e. the tables to which we _subscribe_. In the snippet above, the subscripion is to the tables of collisions and tracks.
+
+Finally, `WorkflowSpec` is necessay to add the task to the DPL workflow . And it looks like:
+
+ ```c
+ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
+{
+ // Equivalent to the AddTask in AliPhysics
+ WorkflowSpec workflow{adaptAnalysisTask(cfgc)};
+ return workflow;
+}
+```
+
+## Second task
+
+We will be using [CFTutorialTask1.cxx](https://github.com/CF-tutorials/O2Physics/blob/tutorial/PWGCF/Tutorial/CFTutorialTask1.cxx). In this part of the tutorial, we will focus on how to access information from different tables. Sets of information stored in different tables can be put together using `Join`:
+
+```c
+namespace o2::aod
+{
+using MyCollision = soa::Join::iterator;
+using MyTracks = soa::Join;
+} // namespace o2::aod
+ ```
+The data model provides some predifined joins, suche as `FullTracks = soa::Join`. The complete list of predefined joins can be found in [ALICE O2 documentation - The Data Model, Joins and iterators](https://aliceo2group.github.io/analysis-framework/docs/datamodel/joinsAndIterators.html).
+
+Joined tables can be normally used as agruments of process.
+
+```c
+ // CFTutorialTask1.cxx version
+ void process(aod::MyCollision const& coll, aod::MyTracks const& inputTracks){...}
+ // CFTutorialTask0.cxx version
+ void process(aod::Collision const& coll, aod::Tracks const& inputTracks){...}
+```
+
+## Third task
+
+We will be using [CFTutorialTask2.cxx](https://github.com/CF-tutorials/O2Physics/blob/tutorial/PWGCF/Tutorial/CFTutorialTask2.cxx). In this part of the tutorial, we will focus on how to use **Configurables** and **Filters**.
+
+A `Configurable` is a parameter of the task, which can be set from command line or in the dedicated space in Hyperloop.
+In the following, an example for the configurable used for the selection on the vertex z coordinate:
+```c
+Configurable ConfZvtxCut{"ConfZvtxCut", 10, "Z vtx cut"};
+```
+In the task, tha value of the configurable can be accesses simply by calling the Configurable itself.
+
+A `Filter` is used to select the rows of a table which satisfy particular requirements. For example:
+```c
+Filter trackFilter = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut);
+```
+Note that configurables can be used in the definition of a filters.
+
+## Fourth task
+
+We will be using [CFTutorialTask3.cxx](https://github.com/CF-tutorials/O2Physics/blob/tutorial/PWGCF/Tutorial/CFTutorialTask3.cxx). In this part of the tutorial, we will focus on how to create and use **partitions**.
+
+A `Partition` is a subset of a given table that satisfies particular requirements. In the following, a partition obtained from the table _Tracks_:
+
+```c
+Partition positive = (nabs(aod::track::eta) < ConfEtaCut) && (aod::track::pt > ConfMinPtCut) && (aod::track::pt < ConfMaxPtCut) && (aod::track::signed1Pt > ConfChargeCut);
+```
+Note that configurables can be used in the definition of a partitions.
+
+Partition can be used in the following way:
+ ```c
+void process(MyFilteredCollision const& coll, o2::aod::Tracks const& tracks)
+{
+
+ ...
+
+ auto groupPositive = positive->sliceByCached(aod::track::collisionId, coll.globalIndex());
+
+ for (auto track : groupPositive) {
+ histos.fill(HIST("hChargePos"), track.sign());
+ }
+
+ ...
+}
+```
+**WARNING**: using a `Partition` of the table `Tracks`, you are considering **ALL** the tracks (which satisfy the requirements) of **ALL** the collisions. In order to select the elements corresponding to the relevant collision, one must use `sliceByCached`, as shown in the example above.
+
+
+
+
+## Fifth task
+
+We will be using [CFTutorialTask4.cxx](https://github.com/CF-tutorials/O2Physics/blob/tutorial/PWGCF/Tutorial/CFTutorialTask4.cxx). In this part of the tutorial, we will focus on how to use and combine elements of different **partitions**. In the following, you can see the invariant mass of two pions, a negative one and a positive one, belonging to two different partitions.
+
+`combinations` returns a pair of elements taken from different partitions. `CombinationsFullIndexPolicy` is needed to combine all the element of the first set with all the element of the second set.
+
+```c
+for (auto& [pos, neg] : combinations(soa::CombinationsFullIndexPolicy(groupPositive, groupNegative))) {
+
+ if (fabs(pos.tpcNSigmaPi()) > 3 or fabs(neg.tpcNSigmaPi()) > 3) {
+ continue;
+ }
+
+ TLorentzVector posVec;
+ posVec.SetPtEtaPhiM(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassPionCharged);
+ TLorentzVector negVec;
+ negVec.SetPtEtaPhiM(neg.pt(), neg.eta(), neg.phi(), o2::constants::physics::MassPionCharged);
+
+ TLorentzVector sumVec(posVec);
+ sumVec += negVec;
+ histos.fill(HIST("hInvariantMass"), sumVec.M());
+ }
+```
+
+
+## Sixth task
+
+We will be using [CFTutorialTask5.cxx](https://github.com/CF-tutorials/O2Physics/blob/tutorial/PWGCF/Tutorial/CFTutorialTask5.cxx). In this part of the tutorial, we will show how to use multiple processes in the same task with **process switches** and how to access and combine elements of different **partitions** and **different events**.
+
+With diss task, we compute the invariant mass of two pions, one positive and one negative.
+We have two separate process functions:
+
+1. `processSameEvent` computes the invariant mass for two pions produced in the same collision.
+
+2. `processMixed` computes the invariant mass for two pions produced in the different collisions.
+
+```c
+void processSameEvent(){}
+PROCESS_SWITCH(, processSameEvent, "Enable processing same event", true);
+
+void processMixed(){}
+PROCESS_SWITCH(, processMixed, "Enable processing mixed event", true);
+```
+
+The process functions can be activated using `PROCESS_SWITCH`, setting the last parameter as `true`. This parameter can be configured via command line, json file or in the hyperloop interface.
diff --git a/PWGCF/Tutorial/run-config.json b/PWGCF/Tutorial/run-config.json
new file mode 100644
index 00000000000..e842cc5394e
--- /dev/null
+++ b/PWGCF/Tutorial/run-config.json
@@ -0,0 +1,134 @@
+{
+ "internal-dpl-clock": "",
+ "internal-dpl-aod-reader": {
+ "time-limit": "0",
+ "orbit-offset-enumeration": "0",
+ "orbit-multiplier-enumeration": "0",
+ "start-value-enumeration": "0",
+ "end-value-enumeration": "-1",
+ "step-value-enumeration": "1",
+ "aod-file": "pippo6.root"
+ },
+ "internal-dpl-aod-index-builder": "",
+ "internal-dpl-aod-spawner": "",
+ "fdd-converter": "",
+ "timestamp-task": {
+ "verbose": "false",
+ "rct-path": "RCT/RunInformation/",
+ "start-orbit-path": "GRP/StartOrbit",
+ "ccdb-url": "http://alice-ccdb.cern.ch",
+ "isRun2MC": "false"
+ },
+ "tpc-pid": {
+ "param-file": "",
+ "param-signal": "BetheBloch",
+ "param-sigma": "TPCReso",
+ "ccdb-url": "http://alice-ccdb.cern.ch",
+ "ccdbPath": "Analysis/PID/TPC/Response",
+ "ccdb-timestamp": "-1",
+ "pid-el": "-1",
+ "pid-mu": "-1",
+ "pid-pi": "-1",
+ "pid-ka": "-1",
+ "pid-pr": "-1",
+ "pid-de": "-1",
+ "pid-tr": "-1",
+ "pid-he": "-1",
+ "pid-al": "-1"
+ },
+ "bc-selection-task": {
+ "processRun2": "false",
+ "processRun3": "true"
+ },
+ "tof-signal": "",
+ "track-propagation": {
+ "ccdb-url": "http://alice-ccdb.cern.ch",
+ "lutPath": "GLO/Param/MatLUT",
+ "geoPath": "GLO/Config/GeometryAligned",
+ "grpPath": "GLO/GRP/GRP",
+ "mVtxPath": "GLO/Calib/MeanVertex",
+ "processStandard": "true",
+ "processCovariance": "false"
+ },
+ "event-selection-task": {
+ "syst": "pp",
+ "muonSelection": "0",
+ "customDeltaBC": "300",
+ "isMC": "false",
+ "processRun2": "false",
+ "processRun3": "true"
+ },
+ "multiplicity-table": {
+ "processRun2": "false",
+ "processRun3": "true"
+ },
+ "track-selection": {
+ "isRun3": "true"
+ },
+ "c-f-tutorial-task0": "",
+ "c-f-tutorial-task1": "",
+ "c-f-tutorial-task2": {
+ "ConfZvtxCut": "10",
+ "ConfEtaCut": "0.800000012",
+ "ConfMaxPtCut": "3",
+ "ConfMinPtCut": "0.5",
+ "ConfMinNSigmaTPCCut": "3"
+ },
+ "c-f-tutorial-task3": {
+ "ConfZvtxCut": "10",
+ "ConfEtaCut": "0.800000012",
+ "ConfMaxPtCut": "3",
+ "ConfMinPtCut": "0.5",
+ "ConfMinNSigmaTPCCut": "3",
+ "ConfChargeCut": "0"
+ },
+ "c-f-tutorial-task4": {
+ "ConfZvtxCut": "10",
+ "ConfEtaCut": "0.800000012",
+ "ConfMaxPtCut": "3",
+ "ConfMinPtCut": "0.5",
+ "ConfMinNSigmaTPCCut": "3",
+ "ConfChargeCut": "0"
+ },
+ "c-f-tutorial-task5": {
+ "ConfZvtxCut": "10",
+ "ConfEtaCut": "0.800000012",
+ "ConfMaxPtCut": "3",
+ "ConfMinPtCut": "0.5",
+ "ConfMinNSigmaTPCCut": "3",
+ "ConfChargeCut": "0",
+ "ConfMultBins": {
+ "values": [
+ "0",
+ "0",
+ "20",
+ "40",
+ "60",
+ "80",
+ "100",
+ "200",
+ "99999"
+ ]
+ },
+ "ConfVtxBins": {
+ "values": [
+ "0",
+ "-10",
+ "-8",
+ "-6",
+ "-4",
+ "-2",
+ "0",
+ "2",
+ "4",
+ "6",
+ "8",
+ "10"
+ ]
+ },
+ "processSame": "true",
+ "processMixed": "true"
+ },
+ "internal-dpl-aod-global-analysis-file-sink": "",
+ "internal-dpl-aod-writer": ""
+}
diff --git a/PWGCF/Tutorial/run.sh b/PWGCF/Tutorial/run.sh
new file mode 100755
index 00000000000..8d6b0fe422f
--- /dev/null
+++ b/PWGCF/Tutorial/run.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+config_file=$1
+
+o2-analysis-timestamp --configuration json://"$config_file" --aod-memory-rate-limit 600000000 |
+ o2-analysis-event-selection --configuration json://"$config_file" --aod-memory-rate-limit 600000000 |
+ o2-analysis-multiplicity-table --configuration json://"$config_file" --aod-memory-rate-limit 600000000 |
+ o2-analysis-track-propagation --configuration json://"$config_file" --aod-memory-rate-limit 600000000 |
+ o2-analysis-pid-tpc --configuration json://"$config_file" --aod-memory-rate-limit 600000000 |
+ o2-analysis-cf-cf-tutorial-5 --configuration json://"$config_file" --aod-memory-rate-limit 600000000