Skip to content

Commit 936e450

Browse files
committed
AOD: Prepare Thinner for TPC-only V0s
This patch carefully checks which tracks are associated with a V0 and flags to not be discarded. This is written such that the thinner is unaware of the track-type. Additionally, this patch further generalizes the thinner by making it check manually for the table_name with regex. E.g., previously the v0-name was specifically ‘O2v0_001’, now this has been changed to ’O2v0_00?’. That way, we no longer care about the actual version contained in the table. Hence, new AO2Ds can also be thinned. Signed-off-by: Felix Schlepper <felix.schlepper@cern.ch>
1 parent 9288e81 commit 936e450

File tree

1 file changed

+137
-50
lines changed

1 file changed

+137
-50
lines changed

Framework/AODMerger/src/aodThinner.cxx

Lines changed: 137 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,29 @@
99
// granted to it by virtue of its status as an Intergovernmental Organization
1010
// or submit itself to any jurisdiction.
1111

12-
#include <map>
13-
#include <list>
14-
#include <fstream>
12+
#include <unordered_map>
1513
#include <getopt.h>
1614

1715
#include "TSystem.h"
16+
#include "TStopwatch.h"
17+
#include "TString.h"
18+
#include "TRegexp.h"
1819
#include "TFile.h"
1920
#include "TTree.h"
21+
#include "TBranch.h"
2022
#include "TList.h"
2123
#include "TKey.h"
2224
#include "TDirectory.h"
2325
#include "TObjString.h"
24-
#include <TGrid.h>
25-
#include <TMap.h>
26-
#include <TLeaf.h>
26+
#include "TGrid.h"
27+
#include "TMap.h"
28+
#include "TLeaf.h"
2729

2830
#include "aodMerger.h"
2931

3032
// AOD reduction tool
3133
// Designed for the 2022 pp data with specific selections:
32-
// - Remove all TPC only tracks
34+
// - Remove all TPC only tracks, optionally keep TPC-only V0 tracks
3335
// - Remove all V0s which refer to any removed track
3436
// - Remove all cascade which refer to any removed V0
3537
// - Remove all ambiguous track entries which point to a track with collision
@@ -38,38 +40,60 @@ int main(int argc, char* argv[])
3840
{
3941
std::string inputFileName("AO2D.root");
4042
std::string outputFileName("AO2D_thinned.root");
41-
int exitCode = 0; // 0: success, >0: failure
43+
int exitCode = 0; // 0: success, !=0: failure
44+
bool bOverwrite = false;
4245

4346
int option_index = 1;
47+
48+
const char* const short_opts = "i:o:KOh";
4449
static struct option long_options[] = {
45-
{"input", required_argument, nullptr, 0},
46-
{"output", required_argument, nullptr, 1},
47-
{"help", no_argument, nullptr, 2},
50+
{"input", required_argument, nullptr, 'i'},
51+
{"output", required_argument, nullptr, 'o'},
52+
{"overwrite", no_argument, nullptr, 'O'},
53+
{"help", no_argument, nullptr, 'h'},
4854
{nullptr, 0, nullptr, 0}};
4955

5056
while (true) {
51-
int c = getopt_long(argc, argv, "", long_options, &option_index);
52-
if (c == -1) {
53-
break;
54-
} else if (c == 0) {
55-
inputFileName = optarg;
56-
} else if (c == 1) {
57-
outputFileName = optarg;
58-
} else if (c == 2) {
59-
printf("AO2D thinning tool. Options: \n");
60-
printf(" --input <inputfile.root> Contains input file path to the file to be thinned. Default: %s\n", inputFileName.c_str());
61-
printf(" --output <outputfile.root> Target output ROOT file. Default: %s\n", outputFileName.c_str());
62-
return -1;
63-
} else {
64-
return -2;
57+
const auto opt = getopt_long(argc, argv, short_opts, long_options, &option_index);
58+
if (opt == -1) {
59+
break; // use defaults
60+
}
61+
switch (opt) {
62+
case 'i':
63+
inputFileName = optarg;
64+
break;
65+
case 'o':
66+
outputFileName = optarg;
67+
break;
68+
case 'O':
69+
bOverwrite = true;
70+
printf("Overwriting existing output file if existing\n");
71+
break;
72+
case 'h':
73+
case '?':
74+
default:
75+
printf("AO2D thinning tool. Options: \n");
76+
printf(" --input/-i <inputfile.root> Contains input file path to the file to be thinned. Default: %s\n", inputFileName.c_str());
77+
printf(" --output/-o <outputfile.root> Target output ROOT file. Default: %s\n", outputFileName.c_str());
78+
printf("\n");
79+
printf(" Optional Arguments:\n");
80+
printf(" --overwrite/-O Overwrite existing output file\n");
81+
return -1;
6582
}
6683
}
6784

6885
printf("AOD reduction started with:\n");
6986
printf(" Input file: %s\n", inputFileName.c_str());
7087
printf(" Ouput file name: %s\n", outputFileName.c_str());
7188

72-
auto outputFile = TFile::Open(outputFileName.c_str(), "RECREATE", "", 501);
89+
TStopwatch clock;
90+
clock.Start(kTRUE);
91+
92+
auto outputFile = TFile::Open(outputFileName.c_str(), (bOverwrite) ? "RECREATE" : "CREATE", "", 501);
93+
if (outputFile == nullptr) {
94+
printf("Error: File %s exists or cannot be created!\n", outputFileName.c_str());
95+
return 1;
96+
}
7397
TDirectory* outputDir = nullptr;
7498

7599
if (inputFileName.find("alien:") == 0) {
@@ -87,18 +111,21 @@ int main(int argc, char* argv[])
87111
keyList->Sort();
88112

89113
for (auto key1 : *keyList) {
114+
// Keep metaData
90115
if (((TObjString*)key1)->GetString().EqualTo("metaData")) {
91116
auto metaData = (TMap*)inputFile->Get("metaData");
92117
outputFile->cd();
93118
metaData->Write("metaData", TObject::kSingleKey);
94119
}
95120

121+
// Keep parentFiles
96122
if (((TObjString*)key1)->GetString().EqualTo("parentFiles")) {
97123
auto parentFiles = (TMap*)inputFile->Get("parentFiles");
98124
outputFile->cd();
99125
parentFiles->Write("parentFiles", TObject::kSingleKey);
100126
}
101127

128+
// Skip everything else, except 'DF_*'
102129
if (!((TObjString*)key1)->GetString().BeginsWith("DF_")) {
103130
continue;
104131
}
@@ -131,15 +158,27 @@ int main(int argc, char* argv[])
131158
}
132159
}
133160

161+
// Scan versions e.g. 001 or 002 ...
162+
TString v0Name{"O2v0_???"}, trkExtraName{"O2trackextra*"};
163+
TRegexp v0Re(v0Name, kTRUE), trkExtraRe(trkExtraName, kTRUE);
164+
for (TObject* obj : *treeList) {
165+
TString st = obj->GetName();
166+
if (st.Index(v0Re) != kNPOS) {
167+
v0Name = st;
168+
} else if (st.Index(trkExtraRe) != kNPOS) {
169+
trkExtraName = st;
170+
}
171+
}
172+
134173
// Certain order needed in order to populate vectors of skipped entries
135-
auto v0Entry = (TObject*)treeList->FindObject("O2v0_001");
174+
auto v0Entry = (TObject*)treeList->FindObject(v0Name);
136175
treeList->Remove(v0Entry);
137176
treeList->AddFirst(v0Entry);
138177

139178
// Prepare maps for track skimming
140-
auto trackExtraTree = (TTree*)inputFile->Get(Form("%s/%s", dfName, "O2trackextra")); // for example we can use this line to access the track table
179+
auto trackExtraTree = (TTree*)inputFile->Get(Form("%s/%s", dfName, trkExtraName.Data()));
141180
if (trackExtraTree == nullptr) {
142-
printf("O2trackextra table not found\n");
181+
printf("%s table not found\n", trkExtraName.Data());
143182
exitCode = 6;
144183
break;
145184
}
@@ -149,27 +188,57 @@ int main(int argc, char* argv[])
149188
exitCode = 7;
150189
break;
151190
}
152-
auto v0s = (TTree*)inputFile->Get(Form("%s/%s", dfName, "O2v0_001"));
191+
auto v0s = (TTree*)inputFile->Get(Form("%s/%s", dfName, v0Name.Data()));
153192
if (v0s == nullptr) {
154-
printf("O2v0_001 table not found\n");
193+
printf("%s table not found\n", v0Name.Data());
155194
exitCode = 8;
156195
break;
157196
}
158197

198+
// We need to loop over the V0s once and flag the prong indices
199+
int trackIdxPos = 0, trackIdxNeg = 0;
200+
std::unordered_map<int, bool> keepV0TPCs;
201+
v0s->SetBranchAddress("fIndexTracks_Pos", &trackIdxPos);
202+
v0s->SetBranchAddress("fIndexTracks_Neg", &trackIdxNeg);
203+
auto nV0s = v0s->GetEntriesFast();
204+
for (int i{0}; i < nV0s; ++i) {
205+
v0s->GetEntry(i);
206+
keepV0TPCs[trackIdxPos] = true;
207+
keepV0TPCs[trackIdxNeg] = true;
208+
}
209+
159210
std::vector<int> acceptedTracks(trackExtraTree->GetEntries(), -1);
160211
std::vector<bool> hasCollision(trackExtraTree->GetEntries(), false);
161212
std::vector<int> keepV0s(v0s->GetEntries(), -1);
162213

163214
uint8_t tpcNClsFindable = 0;
215+
bool bTPClsFindable = false;
164216
uint8_t ITSClusterMap = 0;
217+
bool bITSClusterMap = false;
165218
uint8_t TRDPattern = 0;
219+
bool bTRDPattern = false;
166220
float_t TOFChi2 = 0;
167-
// char16_t fTPCNClsFindableMinusFound = 0;
168-
trackExtraTree->SetBranchAddress("fTPCNClsFindable", &tpcNClsFindable);
169-
trackExtraTree->SetBranchAddress("fITSClusterMap", &ITSClusterMap);
170-
trackExtraTree->SetBranchAddress("fTRDPattern", &TRDPattern);
171-
trackExtraTree->SetBranchAddress("fTOFChi2", &TOFChi2);
172-
// trackExtraTree->SetBranchAddress("fTPCNClsFindableMinusFound", &fTPCNClsFindableMinusFound);
221+
bool bTOFChi2 = false;
222+
223+
// Test if track properties exist
224+
TBranch* br = nullptr;
225+
TIter next(trackExtraTree->GetListOfBranches());
226+
while ((br = (TBranch*)next())) {
227+
TString brName = br->GetName();
228+
if (brName == "fTPCNClsFindable") {
229+
trackExtraTree->SetBranchAddress("fTPCNClsFindable", &tpcNClsFindable);
230+
bTPClsFindable = true;
231+
} else if (brName == "fITSClusterMap") {
232+
trackExtraTree->SetBranchAddress("fITSClusterMap", &ITSClusterMap);
233+
bITSClusterMap = true;
234+
} else if (brName == "fTRDPattern") {
235+
trackExtraTree->SetBranchAddress("fTRDPattern", &TRDPattern);
236+
bTRDPattern = true;
237+
} else if (brName == "fTOFChi2") {
238+
trackExtraTree->SetBranchAddress("fTOFChi2", &TOFChi2);
239+
bTOFChi2 = true;
240+
}
241+
}
173242

174243
int fIndexCollisions = 0;
175244
track_iu->SetBranchAddress("fIndexCollisions", &fIndexCollisions);
@@ -181,28 +250,34 @@ int main(int argc, char* argv[])
181250
trackExtraTree->GetEntry(i);
182251
track_iu->GetEntry(i);
183252

184-
// Remove TPC only tracks
185-
if (tpcNClsFindable > 0. && ITSClusterMap == 0 && TRDPattern == 0 && TOFChi2 < -1.) {
253+
// Flag collisions
254+
hasCollision[i] = (fIndexCollisions >= 0);
255+
256+
// Remove TPC only tracks, if (opt.) they are not assoc. to a V0
257+
if ((!bTPClsFindable || tpcNClsFindable > 0.) &&
258+
(!bITSClusterMap || ITSClusterMap == 0) &&
259+
(!bTRDPattern || TRDPattern == 0) &&
260+
(!bTOFChi2 || TOFChi2 < -1.) &&
261+
(keepV0TPCs.find(i) == keepV0TPCs.end())) {
186262
counter++;
187263
} else {
188264
acceptedTracks[i] = i - counter;
189265
}
190-
hasCollision[i] = (fIndexCollisions >= 0);
191266
}
192267

193268
for (auto key2 : *treeList) {
194-
auto treeName = ((TObjString*)key2)->GetString().Data();
195-
196-
auto inputTree = (TTree*)inputFile->Get(Form("%s/%s", dfName, treeName));
197-
printf(" Processing tree %s with %lld entries with total size %lld\n", treeName, inputTree->GetEntries(), inputTree->GetTotBytes());
269+
TString treeName = ((TObjString*)key2)->GetString().Data();
198270

199271
// Connect trees but do not copy entries (using the clone function)
200272
// NOTE Basket size etc. are copied in CloneTree()
201-
if (!outputDir) {
273+
if (outputDir == nullptr) {
202274
outputDir = outputFile->mkdir(dfName);
203275
printf("Writing to output folder %s\n", dfName);
204276
}
205277
outputDir->cd();
278+
279+
auto inputTree = (TTree*)inputFile->Get(Form("%s/%s", dfName, treeName.Data()));
280+
printf(" Processing tree %s with %lld entries with total size %lld\n", treeName.Data(), inputTree->GetEntries(), inputTree->GetTotBytes());
206281
auto outputTree = inputTree->CloneTree(0);
207282
outputTree->SetAutoFlush(0);
208283

@@ -213,7 +288,7 @@ int main(int argc, char* argv[])
213288
for (int i = 0; i < branches->GetEntriesFast(); ++i) {
214289
TBranch* br = (TBranch*)branches->UncheckedAt(i);
215290
TString branchName(br->GetName());
216-
TString tableName(getTableName(branchName, treeName));
291+
TString tableName(getTableName(branchName, treeName.Data()));
217292
// register index of track index ONLY
218293
if (!tableName.EqualTo("O2track")) {
219294
continue;
@@ -243,10 +318,10 @@ int main(int argc, char* argv[])
243318
}
244319
}
245320

246-
bool processingTracks = memcmp(treeName, "O2track", 7) == 0; // matches any of the track tables
247-
bool processingCascades = memcmp(treeName, "O2cascade", 9) == 0;
248-
bool processingV0s = memcmp(treeName, "O2v0", 4) == 0;
249-
bool processingAmbiguousTracks = memcmp(treeName, "O2ambiguoustrack", 16) == 0;
321+
bool processingTracks = treeName.BeginsWith("O2track"); // matches any of the track tables
322+
bool processingCascades = treeName.BeginsWith("O2cascade");
323+
bool processingV0s = treeName.BeginsWith("O2v0");
324+
bool processingAmbiguousTracks = treeName.BeginsWith("O2ambiguoustrack");
250325

251326
auto indexV0s = -1;
252327
if (processingCascades) {
@@ -337,6 +412,18 @@ int main(int argc, char* argv[])
337412
gSystem->Unlink(outputFile->GetName());
338413
}
339414

415+
clock.Stop();
416+
417+
// Report savings
418+
auto sBefore = inputFile->GetSize();
419+
auto sAfter = outputFile->GetSize();
420+
if (sBefore <= 0 || sAfter <= 0) {
421+
printf("Warning: Empty input or output file after thinning!\n");
422+
exitCode = 9;
423+
}
424+
auto spaceSaving = (1 - ((double)sAfter) / ((double)sBefore)) * 100;
425+
printf("Stats: After=%lld / Before=%lld Bytes ---> Saving %.1f%% diskspace!\n", sAfter, sBefore, spaceSaving);
426+
printf("Timing: CPU=%.2f (s); Real=%.2f (s)\n", clock.CpuTime(), clock.RealTime());
340427
printf("End of AOD thinning.\n");
341428

342429
return exitCode;

0 commit comments

Comments
 (0)