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,66 @@ 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 bV0TPC = false , 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+ {" keep-v0-tpc-only-tracks" , no_argument, nullptr , ' K' },
53+ {" overwrite" , no_argument, nullptr , ' O' },
54+ {" help" , no_argument, nullptr , ' h' },
4855 {nullptr , 0 , nullptr , 0 }};
4956
5057 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 ;
58+ const auto opt = getopt_long (argc, argv, short_opts, long_options, &option_index);
59+ if (opt == -1 ) {
60+ break ; // use defaults
61+ }
62+ switch (opt) {
63+ case ' i' :
64+ inputFileName = optarg;
65+ break ;
66+ case ' o' :
67+ outputFileName = optarg;
68+ break ;
69+ case ' K' :
70+ bV0TPC = true ;
71+ printf (" Keeping TPC-only tracks associated with V0s\n " );
72+ break ;
73+ case ' O' :
74+ bOverwrite = true ;
75+ printf (" Overwriting existing output file if existing\n " );
76+ break ;
77+ case ' h' :
78+ case ' ?' :
79+ default :
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 (" \n " );
84+ printf (" Optional Arguments:\n " );
85+ printf (" --overwrite/-O Overwrite existing output file\n " );
86+ printf (" --keep-v0-tpc-only-tracks/-K Keep tracks associated to TPC-only V0s\n " );
87+ return -1 ;
6588 }
6689 }
6790
6891 printf (" AOD reduction started with:\n " );
6992 printf (" Input file: %s\n " , inputFileName.c_str ());
7093 printf (" Ouput file name: %s\n " , outputFileName.c_str ());
7194
72- auto outputFile = TFile::Open (outputFileName.c_str (), " RECREATE" , " " , 501 );
95+ TStopwatch clock;
96+ clock.Start (kTRUE );
97+
98+ auto outputFile = TFile::Open (Form (" %s" , outputFileName.c_str ()), (bOverwrite) ? " RECREATE" : " CREATE" , " " , 501 );
99+ if (outputFile == nullptr ) {
100+ printf (" Error: File %s exists or cannot be created!\n " , outputFileName.c_str ());
101+ return 1 ;
102+ }
73103 TDirectory* outputDir = nullptr ;
74104
75105 if (inputFileName.find (" alien:" ) == 0 ) {
@@ -87,18 +117,21 @@ int main(int argc, char* argv[])
87117 keyList->Sort ();
88118
89119 for (auto key1 : *keyList) {
120+ // Keep metaData
90121 if (((TObjString*)key1)->GetString ().EqualTo (" metaData" )) {
91122 auto metaData = (TMap*)inputFile->Get (" metaData" );
92123 outputFile->cd ();
93124 metaData->Write (" metaData" , TObject::kSingleKey );
94125 }
95126
127+ // Keep parentFiles
96128 if (((TObjString*)key1)->GetString ().EqualTo (" parentFiles" )) {
97129 auto parentFiles = (TMap*)inputFile->Get (" parentFiles" );
98130 outputFile->cd ();
99131 parentFiles->Write (" parentFiles" , TObject::kSingleKey );
100132 }
101133
134+ // Skip everything else, except 'DF_*'
102135 if (!((TObjString*)key1)->GetString ().BeginsWith (" DF_" )) {
103136 continue ;
104137 }
@@ -131,15 +164,27 @@ int main(int argc, char* argv[])
131164 }
132165 }
133166
167+ // Scan versions e.g. 001 or 002 ...
168+ TString v0Name{" O2v0_???" }, trkExtraName{" O2trackextra*" };
169+ TRegexp v0Re (v0Name, kTRUE ), trkExtraRe (trkExtraName, kTRUE );
170+ for (TObject* obj : *treeList) {
171+ TString st = obj->GetName ();
172+ if (st.Index (v0Re) != kNPOS ) {
173+ v0Name = st;
174+ } else if (st.Index (trkExtraRe) != kNPOS ) {
175+ trkExtraName = st;
176+ }
177+ }
178+
134179 // Certain order needed in order to populate vectors of skipped entries
135- auto v0Entry = (TObject*)treeList->FindObject (" O2v0_001 " );
180+ auto v0Entry = (TObject*)treeList->FindObject (v0Name );
136181 treeList->Remove (v0Entry);
137182 treeList->AddFirst (v0Entry);
138183
139184 // 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
185+ auto trackExtraTree = (TTree*)inputFile->Get (Form (" %s/%s" , dfName, trkExtraName. Data ()));
141186 if (trackExtraTree == nullptr ) {
142- printf (" O2trackextra table not found\n " );
187+ printf (" %s table not found\n " , trkExtraName. Data () );
143188 exitCode = 6 ;
144189 break ;
145190 }
@@ -149,27 +194,60 @@ int main(int argc, char* argv[])
149194 exitCode = 7 ;
150195 break ;
151196 }
152- auto v0s = (TTree*)inputFile->Get (Form (" %s/%s" , dfName, " O2v0_001 " ));
197+ auto v0s = (TTree*)inputFile->Get (Form (" %s/%s" , dfName, v0Name. Data () ));
153198 if (v0s == nullptr ) {
154- printf (" O2v0_001 table not found\n " );
199+ printf (" %s table not found\n " , v0Name. Data () );
155200 exitCode = 8 ;
156201 break ;
157202 }
158203
204+ std::unordered_map<int , bool > keepV0TPCs;
205+ if (bV0TPC) {
206+ // We need to loop over the V0s once and flag the prong indices
207+ // technically we need to flag only V0s with TPC-only prongs but
208+ // this requires in-depth knowledge of the data-model which we don't need.
209+ int trackIdxPos = 0 , trackIdxNeg = 0 ;
210+ v0s->SetBranchAddress (" fIndexTracks_Pos" , &trackIdxPos);
211+ v0s->SetBranchAddress (" fIndexTracks_Neg" , &trackIdxNeg);
212+ for (int i{0 }; v0s->LoadTree (i) >= 0 ; ++i) {
213+ v0s->GetEntry (i);
214+ keepV0TPCs[trackIdxPos] = true ;
215+ keepV0TPCs[trackIdxNeg] = true ;
216+ }
217+ }
218+
159219 std::vector<int > acceptedTracks (trackExtraTree->GetEntries (), -1 );
160220 std::vector<bool > hasCollision (trackExtraTree->GetEntries (), false );
161221 std::vector<int > keepV0s (v0s->GetEntries (), -1 );
162222
163223 uint8_t tpcNClsFindable = 0 ;
224+ bool bTPClsFindable = false ;
164225 uint8_t ITSClusterMap = 0 ;
226+ bool bITSClusterMap = false ;
165227 uint8_t TRDPattern = 0 ;
228+ bool bTRDPattern = false ;
166229 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);
230+ bool bTOFChi2 = false ;
231+
232+ // Test if track properties exist
233+ TBranch* br = nullptr ;
234+ TIter next (trackExtraTree->GetListOfBranches ());
235+ while ((br = (TBranch*)next ())) {
236+ TString brName = br->GetName ();
237+ if (brName == " fTPCNClsFindable" ) {
238+ trackExtraTree->SetBranchAddress (" fTPCNClsFindable" , &tpcNClsFindable);
239+ bTPClsFindable = true ;
240+ } else if (brName == " fITSClusterMap" ) {
241+ trackExtraTree->SetBranchAddress (" fITSClusterMap" , &ITSClusterMap);
242+ bITSClusterMap = true ;
243+ } else if (brName == " fTRDPattern" ) {
244+ trackExtraTree->SetBranchAddress (" fTRDPattern" , &TRDPattern);
245+ bTRDPattern = true ;
246+ } else if (brName == " fTOFChi2" ) {
247+ trackExtraTree->SetBranchAddress (" fTOFChi2" , &TOFChi2);
248+ bTOFChi2 = true ;
249+ }
250+ }
173251
174252 int fIndexCollisions = 0 ;
175253 track_iu->SetBranchAddress (" fIndexCollisions" , &fIndexCollisions );
@@ -181,20 +259,23 @@ int main(int argc, char* argv[])
181259 trackExtraTree->GetEntry (i);
182260 track_iu->GetEntry (i);
183261
184- // Remove TPC only tracks
185- if (tpcNClsFindable > 0 . && ITSClusterMap == 0 && TRDPattern == 0 && TOFChi2 < -1 .) {
262+ // Flag collisions
263+ hasCollision[i] = (fIndexCollisions >= 0 );
264+
265+ // Remove TPC only tracks, if (opt.) they are not assoc. to a V0
266+ if ((!bTPClsFindable || tpcNClsFindable > 0 .) &&
267+ (!bITSClusterMap || ITSClusterMap == 0 ) &&
268+ (!bTRDPattern || TRDPattern == 0 ) &&
269+ (!bTOFChi2 || TOFChi2 < -1 .) &&
270+ (!bV0TPC || keepV0TPCs.find (i) == keepV0TPCs.end ())) {
186271 counter++;
187272 } else {
188273 acceptedTracks[i] = i - counter;
189274 }
190- hasCollision[i] = (fIndexCollisions >= 0 );
191275 }
192276
193277 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 ());
278+ TString treeName = ((TObjString*)key2)->GetString ().Data ();
198279
199280 // Connect trees but do not copy entries (using the clone function)
200281 // NOTE Basket size etc. are copied in CloneTree()
@@ -203,6 +284,9 @@ int main(int argc, char* argv[])
203284 printf (" Writing to output folder %s\n " , dfName);
204285 }
205286 outputDir->cd ();
287+
288+ auto inputTree = (TTree*)inputFile->Get (Form (" %s/%s" , dfName, treeName.Data ()));
289+ printf (" Processing tree %s with %lld entries with total size %lld\n " , treeName.Data (), inputTree->GetEntries (), inputTree->GetTotBytes ());
206290 auto outputTree = inputTree->CloneTree (0 );
207291 outputTree->SetAutoFlush (0 );
208292
@@ -213,7 +297,7 @@ int main(int argc, char* argv[])
213297 for (int i = 0 ; i < branches->GetEntriesFast (); ++i) {
214298 TBranch* br = (TBranch*)branches->UncheckedAt (i);
215299 TString branchName (br->GetName ());
216- TString tableName (getTableName (branchName, treeName));
300+ TString tableName (getTableName (branchName, treeName. Data () ));
217301 // register index of track index ONLY
218302 if (!tableName.EqualTo (" O2track" )) {
219303 continue ;
@@ -243,10 +327,10 @@ int main(int argc, char* argv[])
243327 }
244328 }
245329
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 ;
330+ bool processingTracks = treeName. BeginsWith ( " O2track" ) ; // matches any of the track tables
331+ bool processingCascades = treeName. BeginsWith ( " O2cascade" ) ;
332+ bool processingV0s = treeName. BeginsWith ( " O2v0" ) ;
333+ bool processingAmbiguousTracks = treeName. BeginsWith ( " O2ambiguoustrack" ) ;
250334
251335 auto indexV0s = -1 ;
252336 if (processingCascades) {
@@ -337,6 +421,18 @@ int main(int argc, char* argv[])
337421 gSystem ->Unlink (outputFile->GetName ());
338422 }
339423
424+ clock.Stop ();
425+
426+ // Report savings
427+ auto sBefore = inputFile->GetSize ();
428+ auto sAfter = outputFile->GetSize ();
429+ if (sBefore <= 0 || sAfter <= 0 ) {
430+ printf (" Warning: Empty input or output file after thinning!\n " );
431+ exitCode = 9 ;
432+ }
433+ auto spaceSaving = (1 - ((double )sAfter ) / ((double )sBefore )) * 100 ;
434+ printf (" Stats: After=%lld / Before=%lld Bytes ---> Saving %.1f%% diskspace!\n " , sAfter , sBefore , spaceSaving);
435+ printf (" Timing: CPU=%.2f (s); Real=%.2f (s)\n " , clock.CpuTime (), clock.RealTime ());
340436 printf (" End of AOD thinning.\n " );
341437
342438 return exitCode;
0 commit comments