Skip to content

Commit 0d98582

Browse files
spucilloalibuild
andauthored
[Tutorial,PWGLF] Add StrangenessV0s hands-on tutorial (#18740)
Co-authored-by: ALICE Action Bot <alibuild@cern.ch>
1 parent c1c4b3a commit 0d98582

23 files changed

Lines changed: 2157 additions & 2 deletions

‎Tutorials/PWGLF/Strangeness/CMakeLists.txt‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,3 @@
1111

1212
add_subdirectory(Original)
1313
add_subdirectory(Derived)
14-
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
# All rights not expressly granted are reserved.
4+
#
5+
# This software is distributed under the terms of the GNU General Public
6+
# License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
#
8+
# In applying this license CERN does not waive the privileges and immunities
9+
# granted to it by virtue of its status as an Intergovernmental Organization
10+
# or submit itself to any jurisdiction.
11+
12+
o2physics_add_dpl_workflow(strangeness-derived-v0-skeleton
13+
SOURCES strangeness_derived_v0_skeleton.cxx
14+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
15+
COMPONENT_NAME AnalysisTutorial)
16+
17+
o2physics_add_dpl_workflow(strangeness-derived-v0-step0
18+
SOURCES strangeness_derived_v0_step0.cxx
19+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
20+
COMPONENT_NAME AnalysisTutorial)
21+
22+
o2physics_add_dpl_workflow(strangeness-derived-v0-step1
23+
SOURCES strangeness_derived_v0_step1.cxx
24+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
25+
COMPONENT_NAME AnalysisTutorial)
26+
27+
o2physics_add_dpl_workflow(strangeness-derived-v0-step2
28+
SOURCES strangeness_derived_v0_step2.cxx
29+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
30+
COMPONENT_NAME AnalysisTutorial)
31+
32+
o2physics_add_dpl_workflow(strangeness-derived-v0-step3
33+
SOURCES strangeness_derived_v0_step3.cxx
34+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
35+
COMPONENT_NAME AnalysisTutorial)
36+
37+
o2physics_add_dpl_workflow(strangeness-derived-v0-step4
38+
SOURCES strangeness_derived_v0_step4.cxx
39+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
40+
COMPONENT_NAME AnalysisTutorial)
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Strangeness hands-on — V0s in pp @ √s = 5.36 TeV (derived data)
2+
3+
In this hands-on session you will write an analysis task for **K0S, Λ and Λbar** on strangeness derived
4+
data of pp collisions at 5.36 TeV, in 5 steps, starting from a skeleton.
5+
6+
You will learn how to:
7+
- use the strangeness derived data and structure your analysis task;
8+
- reconstruct K0S, Λ and Λbar;
9+
- apply topological, kinematic and PID (TPC and TOF) selections;
10+
- access the MC information of the V0s to compute the efficiency.
11+
12+
## Preparation
13+
14+
Update O2Physics and enter the environment:
15+
16+
```bash
17+
alienv enter O2Physics/latest
18+
which o2-analysistutorial-lf-strangeness-derived-v0-skeleton # must exist
19+
which o2-analysistutorial-lf-strangeness-derived-v0-step0 # checkpoint
20+
```
21+
22+
Go to the folder of the hands-on in your O2Physics source directory:
23+
24+
```bash
25+
cd ~/alice/O2Physics/Tutorials/PWGLF/Strangeness/Derived/AnalysisV0
26+
```
27+
28+
Download the two input files (strangeness derived data, ~<200> MB each):
29+
30+
```bash
31+
mkdir -p data MC
32+
# real data -> data/AO2D.root (used by skeleton and steps 0-3)
33+
rsync -r -u --progress USERNAME@lxplus.cern.ch:/eos/user/<u>/<user>/tutorialData/data/AO2D.root data
34+
# Monte Carlo -> MC/AO2D.root (used by step 4, which needs the MC truth)
35+
rsync -r -u --progress USERNAME@lxplus.cern.ch:/eos/user/<u>/<user>/tutorialData/MC/AO2D.root MC
36+
```
37+
38+
Alternatively, download them from CERNBox links.
39+
40+
The run scripts do not contain the file paths: they read them from two text files (one path per line,
41+
relative to this folder or absolute), which you have to create (the paths are also given in the slides):
42+
43+
```bash
44+
echo "data/AO2D.root" > input_data.txt # used by runSkeleton.sh, runStep0-3.sh
45+
echo "MC/AO2D.root" > input_MC.txt # used by runStep4.sh
46+
```
47+
48+
If you save the files elsewhere, edit these two text files. To run steps 0-3 on the MC (e.g. to compare
49+
with data): `INPUTFILE=input_MC.txt ./runStep2.sh`.
50+
51+
## How to work
52+
53+
The code you will modify is the skeleton in this folder:
54+
`Tutorials/PWGLF/Strangeness/Derived/AnalysisV0/strangeness_derived_v0_skeleton.cxx`.
55+
The checkpoints of each step are in the same folder (`strangeness_derived_v0_stepX.cxx`).
56+
57+
```bash
58+
./runSkeleton.sh # "No problems!" must be printed
59+
root -l results/skeleton/AnalysisResults.root
60+
```
61+
62+
After modifying and recompiling the skeleton, run it with the configuration of a given step:
63+
64+
```bash
65+
TASK=o2-analysistutorial-lf-strangeness-derived-v0-skeleton ./runStep1.sh
66+
```
67+
68+
Without `TASK=...` the checkpoint of the step is run: useful to compare with your results.
69+
70+
| Step | What is added | Tables | What to look at |
71+
|----------|-----------------------------------------------------------------------------------------|-------------------------------------------------|-------------------------------------------------|
72+
| skeleton | loop over events, sel8, \|z\| < 10 cm | `StraCollisions`, `StraEvSels` | `eventSelection/hVertexZRec` |
73+
| 0 | loop over V0s, K0S, Λ, Λbar invariant masses | `V0Cores` | `kzeroShort/hMassK0Short`, `lambda/hMassLambda` |
74+
| 1 | topological selections (`Filter` + dynamic columns), rapidity, competing mass rejection | `V0Cores` | `hMass*Selected`, `hV0CosPA`, `hV0Radius` |
75+
| 2 | TPC PID of the daughters (`posTrackExtra_as<>`) | `V0Extras`, `DauTrackExtras`, `DauTrackTPCPIDs` | `hNSigmaPos/NegPionFromK0s`, `hMass*Selected` |
76+
| 3 | TOF PID optimised for secondary particles (`strangenesstofpid` task) | `V0TOFNSigmas` | `hMass*SelectedWithTOF` |
77+
| 4 | MC truth: true reconstructed and generated V0s | `V0CoreMCLabels`, `V0MCCores`, `V0MCCollRefs` | `hPt*TrueRec`, `genParticles/hPt*Gen` |
78+
79+
Step 1 selections (in `jsonConfigs/step1.json`):
80+
81+
| Selection | Value | Getter | Column |
82+
|------------------------------|-------------|--------------------------------|------------------------------|
83+
| DCA pos./neg. daughter to PV | > 0.06 cm | `dcapostopv()`, `dcanegtopv()` | standard |
84+
| DCA between daughters | < 0.5 cm | `dcaV0daughters()` | standard |
85+
| cosine of pointing angle | > 0.995 | `v0cosPA()` | **standard** in derived data |
86+
| decay radius | > 0.9 cm | `v0radius()` | dynamic |
87+
| rapidity | \|y\| < 0.5 | `yK0Short()`, `yLambda()` | dynamic |
88+
89+
Hints for the steps:
90+
- **Step 0**: look at the `aod::V0Cores` table in `PWGLF/DataModel/LFStrangenessTables.h` and at the getters
91+
`.mK0Short()`, `.mLambda()`, `.mAntiLambda()`.
92+
- **Step 1**: keep only standard V0s (`v0Type() == 1`); a `Filter` cannot cut on dynamic columns.
93+
- **Step 2**: K0S → π⁺π⁻, Λ → pπ⁻, Λbar → p̄π⁺. You need `LFStrangenessPIDTables.h`.
94+
- **Step 3**: look at `.positiveHasTOF()`, `.negativeHasTOF()` and `.tofNSigmaK0PiPlus()`, `.tofNSigmaLaPr()`, … in `LFStrangenessPIDTables.h`.
95+
- **Step 4**: `has_v0MCCore()`, `v0MCCore_as<aod::V0MCCores>()`, `.pdgCode()` with `PDG_t::kK0Short`,
96+
`PDG_t::kLambda0`, `PDG_t::kLambda0Bar`; select only primary particles (`.isPhysicalPrimary()`).
97+
98+
## Checkpoints
99+
100+
- **Step 0**: peaks at ~0.497 GeV/c² (K0S) and ~1.116 GeV/c² (Λ, Λbar), already visible thanks to the derived data pre-selections.
101+
- **Step 1**: the background is reduced.
102+
- **Step 2**: the Nσ bands of the pions from K0S are centred at 0; the background under the Λ and Λbar peaks is further reduced.
103+
- **Step 3**: TOF further suppresses the combinatorial background.
104+
- **Step 4**: `hPt*TrueRec / hPt*Gen` gives the efficiency (× acceptance) as a function of pT.
105+
106+
## If something does not run
107+
108+
```bash
109+
root -l -b -q "checkTables.C(\"$(cat input_data.txt)\")" # data: step 4 "CANNOT run" is expected
110+
root -l -b -q "checkTables.C(\"$(cat input_MC.txt)\")" # MC: all steps must be ok
111+
```
112+
113+
The macro tells you which steps can run with the file and, if a table has an old version, which converter
114+
to use:
115+
116+
```bash
117+
CONVERTERS='o2-analysis-lf-straevselsconverter6 ${OPTION} | ' ./runStep0.sh
118+
```
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \file checkTables.C
13+
/// \brief Checks which strangeness tables (and which versions) are present in a derived AO2D
14+
/// and reports which steps of the V0 tutorial (o2-analysistutorial-lf-strangeness-derived-v0-*) can run.
15+
///
16+
/// Usage: root -l -b -q 'checkTables.C("data/AO2D.root")' or something like root -l -b -q "checkTables.C(\"$(cat input_data.txt)\")"
17+
18+
#include <TCollection.h>
19+
#include <TDirectory.h>
20+
#include <TFile.h>
21+
#include <TKey.h>
22+
#include <TString.h>
23+
24+
#include <algorithm>
25+
#include <iostream>
26+
#include <string>
27+
#include <vector>
28+
29+
struct RequiredTable {
30+
std::string base; // tree name without version, e.g. "o2straevsels"
31+
int version{0}; // version required by the current O2Physics data model
32+
int firstStep{-1}; // first step that uses it (-1 = skeleton)
33+
std::string hint; // converter to use if the version in the file is older
34+
};
35+
36+
int findVersion(TDirectory* dir, const std::string& base)
37+
{
38+
int found = -1;
39+
TIter next(dir->GetListOfKeys());
40+
while (TKey* key = dynamic_cast<TKey*>(next())) {
41+
TString name = key->GetName();
42+
name.ToLower();
43+
if (name == base.c_str()) {
44+
found = std::max(found, 0);
45+
} else if (name.BeginsWith((base + "_").c_str())) {
46+
TString v = name(base.size() + 1, name.Length());
47+
if (v.IsDigit()) {
48+
found = std::max(found, v.Atoi());
49+
}
50+
}
51+
}
52+
return found;
53+
}
54+
55+
void checkTables(const char* fname = "data/AO2D.root")
56+
{
57+
const std::vector<RequiredTable> required = {
58+
{.base = "o2stracollision", .version = 0, .firstStep = -1, .hint = ""},
59+
{.base = "o2straevsels", .version = 6, .firstStep = -1, .hint = "o2-analysis-lf-straevselsconverterN (chain up to straevselsconverter6)"},
60+
{.base = "o2v0core", .version = 0, .firstStep = 0, .hint = ""},
61+
{.base = "o2v0extra", .version = 0, .firstStep = 2, .hint = ""},
62+
{.base = "o2dautrackextra", .version = 3, .firstStep = 2, .hint = "o2-analysis-lf-stradautracksextraconverterN (up to stradautracksextraconverter3)"},
63+
{.base = "o2dautracktpcpid", .version = 1, .firstStep = 2, .hint = "o2-analysis-lf-stradautrackstpcpidconverter"},
64+
// step 3: tables read as input by the helper task strangenesstofpid,
65+
// not by the analysis itself: the tutorial only uses V0s
66+
{.base = "o2strastamps", .version = 1, .firstStep = 3, .hint = "o2-analysis-lf-strastampsconverter"},
67+
{.base = "o2straevtimes", .version = 1, .firstStep = 3, .hint = "o2-analysis-lf-stradautrackstofpidconverter3"},
68+
{.base = "o2dautracktofpid", .version = 3, .firstStep = 3, .hint = "o2-analysis-lf-stradautrackstofpidconverterN (up to stradautrackstofpidconverter4)"},
69+
{.base = "o2v0collref", .version = 0, .firstStep = 3, .hint = ""},
70+
{.base = "o2casccore", .version = 0, .firstStep = 3, .hint = ""},
71+
{.base = "o2cascextra", .version = 0, .firstStep = 3, .hint = ""},
72+
{.base = "o2casccollref", .version = 0, .firstStep = 3, .hint = ""},
73+
// step 4: MC
74+
{.base = "o2v0coremclabel", .version = 0, .firstStep = 4, .hint = ""},
75+
{.base = "o2v0mccore", .version = 2, .firstStep = 4, .hint = "o2-analysis-lf-v0coresconverter / v0coresconverter2"},
76+
{.base = "o2v0mccollref", .version = 0, .firstStep = 4, .hint = ""},
77+
{.base = "o2stracolllabels", .version = 0, .firstStep = 4, .hint = ""},
78+
{.base = "o2stramccollision", .version = 2, .firstStep = 4, .hint = "o2-analysis-lf-stramccollisionconverter / stramccollisionconverter2"},
79+
};
80+
81+
TFile* f = TFile::Open(fname);
82+
if (!f || f->IsZombie()) {
83+
std::cout << "Cannot open " << fname << std::endl;
84+
return;
85+
}
86+
TDirectory* df = nullptr;
87+
TIter next(f->GetListOfKeys());
88+
while (TKey* key = dynamic_cast<TKey*>(next())) {
89+
if (TString(key->GetName()).BeginsWith("DF_")) {
90+
df = f->GetDirectory(key->GetName());
91+
break;
92+
}
93+
}
94+
if (!df) {
95+
std::cout << "No DF_* folder in " << fname << std::endl;
96+
return;
97+
}
98+
99+
std::cout << "\nTrees in " << df->GetName() << ":\n";
100+
TIter nextTree(df->GetListOfKeys());
101+
while (TKey* key = dynamic_cast<TKey*>(nextTree())) {
102+
std::cout << " " << key->GetName() << "\n";
103+
}
104+
105+
int firstBrokenStep = 99; // first step that cannot run, even with converters
106+
int firstConverterStep = 99; // first step that needs converters
107+
std::cout << "\nTables required by the tutorial:\n";
108+
for (const auto& t : required) {
109+
int v = findVersion(df, t.base);
110+
TString status = "ok";
111+
if (v < 0) {
112+
status = "MISSING";
113+
firstBrokenStep = std::min(firstBrokenStep, t.firstStep);
114+
} else if (v < t.version) {
115+
status = Form("v%d < v%d required", v, t.version);
116+
firstConverterStep = std::min(firstConverterStep, t.firstStep);
117+
} else if (v > t.version) {
118+
status = Form("v%d > v%d: O2Physics is older than the data, update it", v, t.version);
119+
firstBrokenStep = std::min(firstBrokenStep, t.firstStep);
120+
}
121+
TString step = t.firstStep < 0 ? "skeleton" : Form("step%d", t.firstStep);
122+
std::cout << Form(" %-20s (from %-8s) %s", t.base.c_str(), step.Data(), status.Data());
123+
if (status != "ok" && !t.hint.empty() && v >= 0) {
124+
std::cout << "\n -> " << t.hint;
125+
}
126+
std::cout << "\n";
127+
}
128+
129+
std::cout << "\nSummary:\n";
130+
for (int s = -1; s <= 4; s++) {
131+
TString name = s < 0 ? "skeleton" : Form("step%d", s);
132+
TString res = s >= firstBrokenStep ? "CANNOT run with this file"
133+
: s >= firstConverterStep ? "ok, but converters are needed"
134+
: "ok";
135+
std::cout << Form(" %-8s %s\n", name.Data(), res.Data());
136+
}
137+
std::cout << std::endl;
138+
f->Close();
139+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"internal-dpl-clock": "",
3+
"internal-dpl-aod-reader": {
4+
"time-limit": "0",
5+
"orbit-offset-enumeration": "0",
6+
"orbit-multiplier-enumeration": "0",
7+
"start-value-enumeration": "0",
8+
"end-value-enumeration": "-1",
9+
"step-value-enumeration": "1",
10+
"aod-file": "@input_data.txt"
11+
},
12+
"internal-dpl-aod-spawner": "",
13+
"strangeness-derived-v0-tutorial": {
14+
"nBins": "100",
15+
"cutzvertex": "10"
16+
},
17+
"internal-dpl-aod-writer": "",
18+
"internal-dpl-aod-global-analysis-file-sink": "",
19+
"internal-dpl-injected-dummy-sink": ""
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"internal-dpl-clock": "",
3+
"internal-dpl-aod-reader": {
4+
"time-limit": "0",
5+
"orbit-offset-enumeration": "0",
6+
"orbit-multiplier-enumeration": "0",
7+
"start-value-enumeration": "0",
8+
"end-value-enumeration": "-1",
9+
"step-value-enumeration": "1",
10+
"aod-file": "@input_data.txt"
11+
},
12+
"internal-dpl-aod-spawner": "",
13+
"strangeness-derived-v0-tutorial": {
14+
"nBins": "100",
15+
"cutzvertex": "10"
16+
},
17+
"internal-dpl-aod-writer": "",
18+
"internal-dpl-aod-global-analysis-file-sink": "",
19+
"internal-dpl-injected-dummy-sink": ""
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"internal-dpl-clock": "",
3+
"internal-dpl-aod-reader": {
4+
"time-limit": "0",
5+
"orbit-offset-enumeration": "0",
6+
"orbit-multiplier-enumeration": "0",
7+
"start-value-enumeration": "0",
8+
"end-value-enumeration": "-1",
9+
"step-value-enumeration": "1",
10+
"aod-file": "@input_data.txt"
11+
},
12+
"strangeness-derived-v0-tutorial": {
13+
"nBins": "100",
14+
"cutzvertex": "10",
15+
"v0setting_dcav0dau": "0.5",
16+
"v0setting_dcapostopv": "0.06",
17+
"v0setting_dcanegtopv": "0.06",
18+
"v0setting_cospa": "0.995",
19+
"v0setting_radius": "0.9",
20+
"v0setting_rapidity": "0.5"
21+
},
22+
"internal-dpl-aod-writer": "",
23+
"internal-dpl-aod-global-analysis-file-sink": "",
24+
"internal-dpl-injected-dummy-sink": ""
25+
}

0 commit comments

Comments
 (0)