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,56 @@ 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+ for (int i{0 }; v0s->LoadTree (i) >= 0 ; ++i) {
204+ v0s->GetEntry (i);
205+ keepV0TPCs[trackIdxPos] = true ;
206+ keepV0TPCs[trackIdxNeg] = true ;
207+ }
208+
159209 std::vector<int > acceptedTracks (trackExtraTree->GetEntries (), -1 );
160210 std::vector<bool > hasCollision (trackExtraTree->GetEntries (), false );
161211 std::vector<int > keepV0s (v0s->GetEntries (), -1 );
162212
163213 uint8_t tpcNClsFindable = 0 ;
214+ bool bTPClsFindable = false ;
164215 uint8_t ITSClusterMap = 0 ;
216+ bool bITSClusterMap = false ;
165217 uint8_t TRDPattern = 0 ;
218+ bool bTRDPattern = false ;
166219 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);
220+ bool bTOFChi2 = false ;
221+
222+ // Test if track properties exist
223+ TBranch* br = nullptr ;
224+ TIter next (trackExtraTree->GetListOfBranches ());
225+ while ((br = (TBranch*)next ())) {
226+ TString brName = br->GetName ();
227+ if (brName == " fTPCNClsFindable" ) {
228+ trackExtraTree->SetBranchAddress (" fTPCNClsFindable" , &tpcNClsFindable);
229+ bTPClsFindable = true ;
230+ } else if (brName == " fITSClusterMap" ) {
231+ trackExtraTree->SetBranchAddress (" fITSClusterMap" , &ITSClusterMap);
232+ bITSClusterMap = true ;
233+ } else if (brName == " fTRDPattern" ) {
234+ trackExtraTree->SetBranchAddress (" fTRDPattern" , &TRDPattern);
235+ bTRDPattern = true ;
236+ } else if (brName == " fTOFChi2" ) {
237+ trackExtraTree->SetBranchAddress (" fTOFChi2" , &TOFChi2);
238+ bTOFChi2 = true ;
239+ }
240+ }
173241
174242 int fIndexCollisions = 0 ;
175243 track_iu->SetBranchAddress (" fIndexCollisions" , &fIndexCollisions );
@@ -181,28 +249,34 @@ int main(int argc, char* argv[])
181249 trackExtraTree->GetEntry (i);
182250 track_iu->GetEntry (i);
183251
184- // Remove TPC only tracks
185- if (tpcNClsFindable > 0 . && ITSClusterMap == 0 && TRDPattern == 0 && TOFChi2 < -1 .) {
252+ // Flag collisions
253+ hasCollision[i] = (fIndexCollisions >= 0 );
254+
255+ // Remove TPC only tracks, if (opt.) they are not assoc. to a V0
256+ if ((!bTPClsFindable || tpcNClsFindable > 0 .) &&
257+ (!bITSClusterMap || ITSClusterMap == 0 ) &&
258+ (!bTRDPattern || TRDPattern == 0 ) &&
259+ (!bTOFChi2 || TOFChi2 < -1 .) &&
260+ (keepV0TPCs.find (i) == keepV0TPCs.end ())) {
186261 counter++;
187262 } else {
188263 acceptedTracks[i] = i - counter;
189264 }
190- hasCollision[i] = (fIndexCollisions >= 0 );
191265 }
192266
193267 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 ());
268+ TString treeName = ((TObjString*)key2)->GetString ().Data ();
198269
199270 // Connect trees but do not copy entries (using the clone function)
200271 // NOTE Basket size etc. are copied in CloneTree()
201- if (! outputDir) {
272+ if (outputDir == nullptr ) {
202273 outputDir = outputFile->mkdir (dfName);
203274 printf (" Writing to output folder %s\n " , dfName);
204275 }
205276 outputDir->cd ();
277+
278+ auto inputTree = (TTree*)inputFile->Get (Form (" %s/%s" , dfName, treeName.Data ()));
279+ printf (" Processing tree %s with %lld entries with total size %lld\n " , treeName.Data (), inputTree->GetEntries (), inputTree->GetTotBytes ());
206280 auto outputTree = inputTree->CloneTree (0 );
207281 outputTree->SetAutoFlush (0 );
208282
@@ -213,7 +287,7 @@ int main(int argc, char* argv[])
213287 for (int i = 0 ; i < branches->GetEntriesFast (); ++i) {
214288 TBranch* br = (TBranch*)branches->UncheckedAt (i);
215289 TString branchName (br->GetName ());
216- TString tableName (getTableName (branchName, treeName));
290+ TString tableName (getTableName (branchName, treeName. Data () ));
217291 // register index of track index ONLY
218292 if (!tableName.EqualTo (" O2track" )) {
219293 continue ;
@@ -243,10 +317,10 @@ int main(int argc, char* argv[])
243317 }
244318 }
245319
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 ;
320+ bool processingTracks = treeName. BeginsWith ( " O2track" ) ; // matches any of the track tables
321+ bool processingCascades = treeName. BeginsWith ( " O2cascade" ) ;
322+ bool processingV0s = treeName. BeginsWith ( " O2v0" ) ;
323+ bool processingAmbiguousTracks = treeName. BeginsWith ( " O2ambiguoustrack" ) ;
250324
251325 auto indexV0s = -1 ;
252326 if (processingCascades) {
@@ -337,6 +411,18 @@ int main(int argc, char* argv[])
337411 gSystem ->Unlink (outputFile->GetName ());
338412 }
339413
414+ clock.Stop ();
415+
416+ // Report savings
417+ auto sBefore = inputFile->GetSize ();
418+ auto sAfter = outputFile->GetSize ();
419+ if (sBefore <= 0 || sAfter <= 0 ) {
420+ printf (" Warning: Empty input or output file after thinning!\n " );
421+ exitCode = 9 ;
422+ }
423+ auto spaceSaving = (1 - ((double )sAfter ) / ((double )sBefore )) * 100 ;
424+ printf (" Stats: After=%lld / Before=%lld Bytes ---> Saving %.1f%% diskspace!\n " , sAfter , sBefore , spaceSaving);
425+ printf (" Timing: CPU=%.2f (s); Real=%.2f (s)\n " , clock.CpuTime (), clock.RealTime ());
340426 printf (" End of AOD thinning.\n " );
341427
342428 return exitCode;
0 commit comments