39int main(
int argc,
char* argv[])
41 std::string inputFileName(
"AO2D.root");
42 std::string outputFileName(
"AO2D_thinned.root");
44 bool bOverwrite =
false;
45 int compression = 505;
49 const char*
const short_opts =
"i:o:KOh";
50 static struct option long_options[] = {
51 {
"input", required_argument,
nullptr,
'i'},
52 {
"output", required_argument,
nullptr,
'o'},
53 {
"overwrite", no_argument,
nullptr,
'O'},
54 {
"compression", required_argument,
nullptr,
'c'},
55 {
"help", no_argument,
nullptr,
'h'},
56 {
nullptr, 0,
nullptr, 0}};
59 const auto opt = getopt_long(argc, argv, short_opts, long_options, &option_index);
65 inputFileName = optarg;
68 outputFileName = optarg;
72 printf(
"Overwriting existing output file if existing\n");
75 compression = atoi(optarg);
80 printf(
"AO2D thinning tool. Options: \n");
81 printf(
" --input/-i <inputfile.root> Contains input file path to the file to be thinned. Default: %s\n", inputFileName.c_str());
82 printf(
" --output/-o <outputfile.root> Target output ROOT file. Default: %s\n", outputFileName.c_str());
83 printf(
" --compression/-c <compression id> ROOT compression algorithm / level. Default: %d\n", compression);
85 printf(
" Optional Arguments:\n");
86 printf(
" --overwrite/-O Overwrite existing output file\n");
91 printf(
"AOD reduction started with:\n");
92 printf(
" Input file: %s\n", inputFileName.c_str());
93 printf(
" Ouput file name: %s\n", outputFileName.c_str());
98 auto outputFile = TFile::Open(outputFileName.c_str(), (bOverwrite) ?
"RECREATE" :
"CREATE",
"", compression);
99 if (outputFile ==
nullptr) {
100 printf(
"Error: File %s exists or cannot be created!\n", outputFileName.c_str());
103 TDirectory* outputDir =
nullptr;
105 if (inputFileName.find(
"alien:") == 0) {
106 printf(
"Connecting to AliEn...");
107 TGrid::Connect(
"alien:");
110 auto inputFile = TFile::Open(inputFileName.c_str());
112 printf(
"Error: Could not open input file %s.\n", inputFileName.c_str());
116 TList* keyList = inputFile->GetListOfKeys();
119 for (
auto key1 : *keyList) {
121 if (((TObjString*)key1)->GetString().EqualTo(
"metaData")) {
122 auto metaData = (TMap*)inputFile->Get(
"metaData");
124 metaData->Write(
"metaData", TObject::kSingleKey);
128 if (((TObjString*)key1)->GetString().EqualTo(
"parentFiles")) {
129 auto parentFiles = (TMap*)inputFile->Get(
"parentFiles");
131 parentFiles->Write(
"parentFiles", TObject::kSingleKey);
135 if (!((TObjString*)key1)->GetString().BeginsWith(
"DF_")) {
139 auto dfName = ((TObjString*)key1)->GetString().Data();
141 printf(
" Processing folder %s\n", dfName);
142 auto folder = (TDirectoryFile*)inputFile->Get(dfName);
143 auto treeList = folder->GetListOfKeys();
147 for (
auto i = 0;
i < treeList->GetEntries(); ++
i) {
148 TKey* ki = (TKey*)treeList->At(
i);
149 for (
int j =
i + 1;
j < treeList->GetEntries(); ++
j) {
150 TKey* kj = (TKey*)treeList->At(
j);
151 if (std::strcmp(ki->GetName(), kj->GetName()) == 0 && std::strcmp(ki->GetTitle(), kj->GetTitle()) == 0) {
152 if (ki->GetCycle() < kj->GetCycle()) {
153 printf(
" *** FATAL *** we had ordered the keys, first cycle should be higher, please check");
157 treeList->Remove(kj);
168 TString v0Name{
"O2v0_???"}, trkExtraName{
"O2trackextra*"};
169 TRegexp v0Re(v0Name, kTRUE), trkExtraRe(trkExtraName, kTRUE);
170 bool hasTrackQA{
false};
171 TString trackQAName{
"O2trackqa*"};
172 TRegexp trackQARe(trackQAName, kTRUE);
173 for (
TObject* obj : *treeList) {
174 TString
st = obj->GetName();
175 if (
st.Index(v0Re) != kNPOS) {
177 }
else if (
st.Index(trkExtraRe) != kNPOS) {
179 }
else if (
st.Index(trackQARe) != kNPOS) {
186 auto v0Entry = (
TObject*)treeList->FindObject(v0Name);
187 treeList->Remove(v0Entry);
188 treeList->AddFirst(v0Entry);
191 auto trackExtraTree = (TTree*)inputFile->Get(Form(
"%s/%s", dfName, trkExtraName.Data()));
192 if (trackExtraTree ==
nullptr) {
193 printf(
"%s table not found\n", trkExtraName.Data());
197 auto track_iu = (TTree*)inputFile->Get(Form(
"%s/%s", dfName,
"O2track_iu"));
198 if (track_iu ==
nullptr) {
199 printf(
"O2track_iu table not found\n");
203 auto v0s = (TTree*)inputFile->Get(Form(
"%s/%s", dfName, v0Name.Data()));
204 if (v0s ==
nullptr) {
205 printf(
"%s table not found\n", v0Name.Data());
211 if (hasTrackQA && (trackQA = (TTree*)inputFile->Get(Form(
"%s/%s", dfName, trackQAName.Data()))) ==
nullptr) {
217 int trackIdxPos = 0, trackIdxNeg = 0;
218 std::vector<bool> keepV0s(trackExtraTree->GetEntries(),
false);
219 v0s->SetBranchAddress(
"fIndexTracks_Pos", &trackIdxPos);
220 v0s->SetBranchAddress(
"fIndexTracks_Neg", &trackIdxNeg);
221 auto nV0s = v0s->GetEntriesFast();
222 for (
int i{0};
i < nV0s; ++
i) {
224 keepV0s[trackIdxPos] =
true;
225 keepV0s[trackIdxNeg] =
true;
229 std::vector<bool> keepTrackQA;
231 keepTrackQA.assign(trackExtraTree->GetEntries(),
false);
232 trackQA->SetBranchAddress(
"fIndexTracks", &trackIdxPos);
233 for (
int i{0};
i < trackQA->GetEntries(); ++
i) {
234 trackQA->GetEntry(
i);
235 keepTrackQA[trackIdxPos] =
true;
239 std::vector<int> acceptedTracks(trackExtraTree->GetEntries(), -1);
240 std::vector<bool> hasCollision(trackExtraTree->GetEntries(),
false);
242 uint8_t tpcNClsFindable = 0;
243 bool bTPClsFindable =
false;
244 uint8_t ITSClusterMap = 0;
245 UInt_t ITSClusterSizes = 0;
246 bool bITSClusterMap =
false;
247 bool bITSClusterSizes =
false;
248 uint8_t TRDPattern = 0;
249 bool bTRDPattern =
false;
251 bool bTOFChi2 =
false;
254 TBranch* br =
nullptr;
255 TIter next(trackExtraTree->GetListOfBranches());
256 while ((br = (TBranch*)next())) {
257 TString brName = br->GetName();
258 if (brName ==
"fTPCNClsFindable") {
259 trackExtraTree->SetBranchAddress(
"fTPCNClsFindable", &tpcNClsFindable);
260 bTPClsFindable =
true;
261 }
else if (brName ==
"fITSClusterMap") {
262 trackExtraTree->SetBranchAddress(
"fITSClusterMap", &ITSClusterMap);
263 bITSClusterMap =
true;
264 }
else if (brName ==
"fITSClusterSizes") {
265 trackExtraTree->SetBranchAddress(
"fITSClusterSizes", &ITSClusterSizes);
266 bITSClusterSizes =
true;
267 }
else if (brName ==
"fTRDPattern") {
268 trackExtraTree->SetBranchAddress(
"fTRDPattern", &TRDPattern);
270 }
else if (brName ==
"fTOFChi2") {
271 trackExtraTree->SetBranchAddress(
"fTOFChi2", &TOFChi2);
278 if (!bTPClsFindable || !bTRDPattern || !bTOFChi2 ||
279 (!bITSClusterMap && !bITSClusterSizes)) {
280 printf(
" *** FATAL *** Branch detection failed in %s for trackextra.[(fITSClusterMap=%d,fITSClusterSizes=%d),fTPCNClsFindable=%d,fTRDPattern=%d,fTOFChi2=%d]\n", dfName, bITSClusterMap, bITSClusterSizes, bTPClsFindable, bTRDPattern, bTOFChi2);
285 int fIndexCollisions = 0;
286 track_iu->SetBranchAddress(
"fIndexCollisions", &fIndexCollisions);
289 auto entries = trackExtraTree->GetEntries();
291 for (
int i = 0;
i < entries;
i++) {
292 trackExtraTree->GetEntry(
i);
293 track_iu->GetEntry(
i);
296 hasCollision[
i] = (fIndexCollisions >= 0);
299 if (tpcNClsFindable > 0 && TRDPattern == 0 && TOFChi2 < -1. &&
300 (!bITSClusterMap || ITSClusterMap == 0) &&
301 (!bITSClusterSizes || ITSClusterSizes == 0) &&
302 (!hasTrackQA || !keepTrackQA[
i]) &&
310 for (
auto key2 : *treeList) {
311 TString treeName = ((TObjString*)key2)->GetString().Data();
315 if (outputDir ==
nullptr) {
316 outputDir = outputFile->mkdir(dfName);
317 printf(
"Writing to output folder %s\n", dfName);
321 auto inputTree = (TTree*)inputFile->Get(Form(
"%s/%s", dfName, treeName.Data()));
322 printf(
" Processing tree %s with %lld entries with total size %lld\n", treeName.Data(), inputTree->GetEntries(), inputTree->GetTotBytes());
323 auto outputTree = inputTree->CloneTree(0);
324 outputTree->SetAutoFlush(0);
326 std::vector<int*> indexList;
327 std::vector<char*> vlaPointers;
328 std::vector<int*> indexPointers;
329 TObjArray* branches = inputTree->GetListOfBranches();
330 for (
int i = 0;
i < branches->GetEntriesFast(); ++
i) {
331 TBranch* br = (TBranch*)branches->UncheckedAt(
i);
332 TString branchName(br->GetName());
333 TString tableName(
getTableName(branchName, treeName.Data()));
335 if (!tableName.EqualTo(
"O2track")) {
339 if (((TLeaf*)br->GetListOfLeaves()->First())->GetLeafCount() !=
nullptr) {
340 printf(
" *** FATAL ***: VLA detection is not supported\n");
342 }
else if (branchName.BeginsWith(
"fIndexSlice")) {
345 vlaPointers.push_back(
reinterpret_cast<char*
>(
buffer));
346 inputTree->SetBranchAddress(br->GetName(),
buffer);
347 outputTree->SetBranchAddress(br->GetName(),
buffer);
349 indexList.push_back(
buffer);
350 indexList.push_back(
buffer + 1);
351 }
else if (branchName.BeginsWith(
"fIndex") && !branchName.EndsWith(
"_size")) {
354 indexPointers.push_back(
buffer);
356 inputTree->SetBranchAddress(br->GetName(),
buffer);
357 outputTree->SetBranchAddress(br->GetName(),
buffer);
359 indexList.push_back(
buffer);
363 const bool processingTracked = treeName.BeginsWith(
"O2tracked");
364 const bool processingTrackQA = treeName.BeginsWith(
"O2trackqa");
365 const bool processingTracks = treeName.BeginsWith(
"O2track") && !processingTracked && !processingTrackQA;
366 const bool processingAmbiguousTracks = treeName.BeginsWith(
"O2ambiguoustrack");
368 auto entries = inputTree->GetEntries();
369 for (
int i = 0;
i < entries;
i++) {
370 inputTree->GetEntry(
i);
371 bool fillThisEntry =
true;
373 if (processingTracks) {
374 if (acceptedTracks[
i] < 0) {
375 fillThisEntry =
false;
379 for (
const auto& idx : indexList) {
380 int oldTrackIndex = *idx;
383 if (oldTrackIndex >= 0) {
384 if (acceptedTracks[oldTrackIndex] < 0) {
385 fillThisEntry =
false;
387 *idx = acceptedTracks[oldTrackIndex];
394 if (processingAmbiguousTracks) {
395 if (hasCollision[
i]) {
396 fillThisEntry =
false;
405 if (entries != outputTree->GetEntries()) {
406 printf(
" Reduced from %lld to %lld entries\n", entries, outputTree->GetEntries());
409 static const std::array<TString, 4> checkNames{
"O2track",
"O2trackextra",
"O2trackcov",
"O2ambiguoustrack"};
410 std::vector<bool> checks(checkNames.size(),
false);
411 for (
size_t i{0};
i < checkNames.size(); ++
i) {
412 if (tableName.EqualTo(checkNames[
i])) {
416 if (std::none_of(checks.begin(), checks.end(), [](
bool b) { return b; })) {
418 printf(
" -> Reduction is not expected for this tree!\n");
425 for (
auto&
buffer : indexPointers) {
428 for (
auto&
buffer : vlaPointers) {
449 printf(
"Removing incomplete output file %s.\n", outputFile->GetName());
450 gSystem->Unlink(outputFile->GetName());
457 auto sBefore = inputFile->GetSize();
458 auto sAfter = outputFile->GetSize();
459 if (sBefore <= 0 || sAfter <= 0) {
460 printf(
"Warning: Empty input or output file after thinning!\n");
463 auto spaceSaving = (1 - ((double)sAfter) / ((double)sBefore)) * 100;
464 printf(
"Stats: After=%lld / Before=%lld Bytes ---> Saving %.1f%% diskspace!\n", sAfter, sBefore, spaceSaving);
465 printf(
"Timing: CPU=%.2f (s); Real=%.2f (s)\n", clock.CpuTime(), clock.RealTime());
466 printf(
"End of AOD thinning.\n");