Skip to content

Commit 4620b42

Browse files
committed
[PWGLF] Add StrangenessV0s hands-on tutorial
1 parent d8d68aa commit 4620b42

22 files changed

Lines changed: 2136 additions & 1 deletion

‎Tutorials/PWGLF/Strangeness/CMakeLists.txt‎

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

1212
add_subdirectory(Original)
1313
add_subdirectory(Derived)
14-
14+
add_subdirectory(StrangenessV0s_HandsOn/o2physics/AnalysisV0)
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+
Clone this repository:
23+
24+
```bash
25+
git clone https://github.com/<USER>StrangenessV0s_HandsOn
26+
cd StrangenessV0s_HandsOn
27+
```
28+
29+
Download the two input files (strangeness derived data, ~<200> MB each):
30+
31+
```bash
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 the repository folder or absolute):
42+
43+
| File | Content | Used by |
44+
|---|---|---|
45+
| `input_data.txt` | `data/AO2D.root` | `runSkeleton.sh`, `runStep0-3.sh` |
46+
| `input_MC.txt` | `MC/AO2D.root` | `runStep4.sh` |
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 O2Physics:
54+
`Tutorials/PWGLF/Strangeness/StrangenessV0s_HandsOn/o2physics/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: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Checks which strangeness tables (and which versions) are present in a derived AO2D
2+
// and reports which steps of the V0 tutorial (o2-analysistutorial-lf-strangeness-derived-v0-*) can run.
3+
//
4+
// Usage: root -l -b -q 'checkTables.C("data/AO2D.root")' or something like root -l -b -q "checkTables.C(\"$(cat input_data.txt)\")"
5+
6+
#include <TDirectory.h>
7+
#include <TFile.h>
8+
#include <TKey.h>
9+
#include <TString.h>
10+
11+
#include <algorithm>
12+
#include <iostream>
13+
#include <string>
14+
#include <vector>
15+
16+
struct RequiredTable {
17+
std::string base; // tree name without version, e.g. "o2straevsels"
18+
int version; // version required by the current O2Physics data model
19+
int firstStep; // first step that uses it (-1 = skeleton)
20+
std::string hint; // converter to use if the version in the file is older
21+
};
22+
23+
int findVersion(TDirectory* dir, const std::string& base)
24+
{
25+
int found = -1;
26+
TIter next(dir->GetListOfKeys());
27+
while (TKey* key = static_cast<TKey*>(next())) {
28+
TString name = key->GetName();
29+
name.ToLower();
30+
if (name == base.c_str()) {
31+
found = std::max(found, 0);
32+
} else if (name.BeginsWith((base + "_").c_str())) {
33+
TString v = name(base.size() + 1, name.Length());
34+
if (v.IsDigit()) {
35+
found = std::max(found, v.Atoi());
36+
}
37+
}
38+
}
39+
return found;
40+
}
41+
42+
void checkTables(const char* fname = "data/AO2D.root")
43+
{
44+
const std::vector<RequiredTable> required = {
45+
{"o2stracollision", 0, -1, ""},
46+
{"o2straevsels", 6, -1, "o2-analysis-lf-straevselsconverterN (chain up to straevselsconverter6)"},
47+
{"o2v0core", 0, 0, ""},
48+
{"o2v0extra", 0, 2, ""},
49+
{"o2dautrackextra", 3, 2, "o2-analysis-lf-stradautracksextraconverterN (up to stradautracksextraconverter3)"},
50+
{"o2dautracktpcpid", 1, 2, "o2-analysis-lf-stradautrackstpcpidconverter"},
51+
// step 3: tables read as input by the helper task strangenesstofpid,
52+
// not by the analysis itself: the tutorial only uses V0s
53+
{"o2strastamps", 1, 3, "o2-analysis-lf-strastampsconverter"},
54+
{"o2straevtimes", 1, 3, "o2-analysis-lf-stradautrackstofpidconverter3"},
55+
{"o2dautracktofpid", 3, 3, "o2-analysis-lf-stradautrackstofpidconverterN (up to stradautrackstofpidconverter4)"},
56+
{"o2v0collref", 0, 3, ""},
57+
{"o2casccore", 0, 3, ""},
58+
{"o2cascextra", 0, 3, ""},
59+
{"o2casccollref", 0, 3, ""},
60+
// step 4: MC
61+
{"o2v0coremclabel", 0, 4, ""},
62+
{"o2v0mccore", 2, 4, "o2-analysis-lf-v0coresconverter / v0coresconverter2"},
63+
{"o2v0mccollref", 0, 4, ""},
64+
{"o2stracolllabels", 0, 4, ""},
65+
{"o2stramccollision", 2, 4, "o2-analysis-lf-stramccollisionconverter / stramccollisionconverter2"},
66+
};
67+
68+
TFile* f = TFile::Open(fname);
69+
if (!f || f->IsZombie()) {
70+
std::cout << "Cannot open " << fname << std::endl;
71+
return;
72+
}
73+
TDirectory* df = nullptr;
74+
TIter next(f->GetListOfKeys());
75+
while (TKey* key = static_cast<TKey*>(next())) {
76+
if (TString(key->GetName()).BeginsWith("DF_")) {
77+
df = f->GetDirectory(key->GetName());
78+
break;
79+
}
80+
}
81+
if (!df) {
82+
std::cout << "No DF_* folder in " << fname << std::endl;
83+
return;
84+
}
85+
86+
std::cout << "\nTrees in " << df->GetName() << ":\n";
87+
TIter nextTree(df->GetListOfKeys());
88+
while (TKey* key = static_cast<TKey*>(nextTree())) {
89+
std::cout << " " << key->GetName() << "\n";
90+
}
91+
92+
int firstBrokenStep = 99; // first step that cannot run, even with converters
93+
int firstConverterStep = 99; // first step that needs converters
94+
std::cout << "\nTables required by the tutorial:\n";
95+
for (const auto& t : required) {
96+
int v = findVersion(df, t.base);
97+
TString status = "ok";
98+
if (v < 0) {
99+
status = "MISSING";
100+
firstBrokenStep = std::min(firstBrokenStep, t.firstStep);
101+
} else if (v < t.version) {
102+
status = Form("v%d < v%d required", v, t.version);
103+
firstConverterStep = std::min(firstConverterStep, t.firstStep);
104+
} else if (v > t.version) {
105+
status = Form("v%d > v%d: O2Physics is older than the data, update it", v, t.version);
106+
firstBrokenStep = std::min(firstBrokenStep, t.firstStep);
107+
}
108+
TString step = t.firstStep < 0 ? "skeleton" : Form("step%d", t.firstStep);
109+
std::cout << Form(" %-20s (from %-8s) %s", t.base.c_str(), step.Data(), status.Data());
110+
if (status != "ok" && !t.hint.empty() && v >= 0) {
111+
std::cout << "\n -> " << t.hint;
112+
}
113+
std::cout << "\n";
114+
}
115+
116+
std::cout << "\nSummary:\n";
117+
for (int s = -1; s <= 4; s++) {
118+
TString name = s < 0 ? "skeleton" : Form("step%d", s);
119+
TString res = s >= firstBrokenStep ? "CANNOT run with this file"
120+
: s >= firstConverterStep ? "ok, but converters are needed"
121+
: "ok";
122+
std::cout << Form(" %-8s %s\n", name.Data(), res.Data());
123+
}
124+
std::cout << std::endl;
125+
f->Close();
126+
}
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_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_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_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+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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_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+
"NSigmaTPCPion": "4",
22+
"NSigmaTPCProton": "4"
23+
},
24+
"internal-dpl-aod-writer": "",
25+
"internal-dpl-aod-global-analysis-file-sink": "",
26+
"internal-dpl-injected-dummy-sink": ""
27+
}

0 commit comments

Comments
 (0)