ATLAS Offline Software
TFileMerger.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 
6 // System include(s):
7 #include <cstring>
8 #include <set>
9 #include <sstream>
10 
11 // ROOT include(s):
12 #include <TFile.h>
13 #include <TFileMergeInfo.h>
14 #include <TMemFile.h>
15 #include <TDirectory.h>
16 #include <TUUID.h>
17 #include <TSystem.h>
18 #include <TString.h>
19 #include <TList.h>
20 #include <TMethodCall.h>
21 #include <TClass.h>
22 #include <TKey.h>
23 #include <TTree.h>
24 #include <TROOT.h>
25 #include <TFunction.h>
26 #include <TTreeCloner.h>
27 
28 // Local include(s):
33 
36 
37 namespace xAOD {
38 
40  : TObject(), m_input(), m_copiedInput(), m_output( 0 ),
41  m_treesToSkip( { "##Shapes", "##Links", "##Params",
42  "##Sections",
43  "CollectionTreeInDet::Track_tlp2",
44  "POOLContainerForm", "POOLContainer",
45  "POOLCollectionTree", "MetaData",
46  "MetaDataHdrForm", "MetaDataHdr" } ),
47  m_events(), m_helperFiles(),
48  m_metaDataToolNames(), m_metaDataTools(),
49  m_mode( TEvent::kClassAccess ), m_entriesToMerge( kBigNumber ),
50  m_verbosity( 0 ) {
51 
52  }
53 
55 
56  // Close all the files that may still be open:
57  closeFiles().ignore();
58  }
59 
61  const std::string& mode ) {
62 
63  // Try to open the file:
64  m_output = ::TFile::Open( name.c_str(), mode.c_str() );
65  if( ! m_output ) {
66  Error( "setOutputFileName",
67  XAOD_MESSAGE( "Couldn't open output file \"%s\" in mode "
68  "\"%s\"" ),
69  name.c_str(), mode.c_str() );
70  return StatusCode::FAILURE;
71  }
72 
73  // Tell the user what happened:
74  Info( "setOutputFileName", "Opened \"%s\" for writing",
75  name.c_str() );
76 
77  // Return ROOT to its main directory in memory:
78  gROOT->cd();
79 
80  // Return gracefully:
81  return StatusCode::SUCCESS;
82  }
83 
84  StatusCode TFileMerger::addFile( const std::string& name,
85  bool copyLocally ) {
86 
87  // Decide whether to use the file from its current location, or to make
88  // a copy of it first:
89  std::string localName = name;
90  if( copyLocally ) {
91  // Helper object to create a unique name for the local file:
92  TUUID uuid;
93  // Construct the local name of he file:
94  localName =
95  TString::Format( "%s/XAODMERGE-%s.root",
96  gSystem->TempDirectory(), uuid.AsString() ).Data();
97  // Copy the file:
98  if( ! TFile::Cp( name.c_str(), localName.c_str(), kTRUE ) ) {
99  Error( "addFile",
100  XAOD_MESSAGE( "Couldn't create local copy of \"%s\" under "
101  "\"%s\"" ),
102  name.c_str(), localName.c_str() );
103  return StatusCode::FAILURE;
104  }
105  // Tell the user what happened:
106  Info( "addFile", "Made a local copy of: %s", name.c_str() );
107  }
108 
109  // Now try to open the file:
110  ::TFile* ifile = ::TFile::Open( localName.c_str(), "READ" );
111  if( ! ifile ) {
112  Error( "addFile", XAOD_MESSAGE( "Couldn't open file \"%s\"" ),
113  localName.c_str() );
114  return StatusCode::FAILURE;
115  }
116 
117  // Add it to our list:
118  m_input.push_back( ifile );
119  m_copiedInput.push_back( copyLocally );
120  Info( "addFile", "Opened \"%s\" for reading", localName.c_str() );
121 
122  // Return ROOT to its main directory in memory:
123  gROOT->cd();
124 
125  // Return gracefully:
126  return StatusCode::SUCCESS;
127  }
128 
141  StatusCode TFileMerger::addMetaDataTool( const std::string& typeName ) {
142 
143  // Check if we already have a tool of this type:
144  if( m_metaDataToolNames.find( typeName ) != m_metaDataToolNames.end() ) {
145  Warning( "addMetaDataTool",
146  "Tool of type \"%s\" is already specified",
147  typeName.c_str() );
148  return StatusCode::RECOVERABLE;
149  }
150 
151  // Try to create an instance of the tool:
152  ::TClass* cl = ::TClass::GetClass( typeName.c_str() );
153  if( ! cl ) {
154  Error( "addMetaDataTool",
155  XAOD_MESSAGE( "Tool of type \"%s\" not found" ),
156  typeName.c_str() );
157  return StatusCode::FAILURE;
158  }
159 
160  // Remember the type:
161  m_metaDataToolNames.insert( typeName );
162 
163  // Tell the user what happened:
164  if( m_verbosity > 1 ) {
165  Info( "addMetaDataTool", "Added tool of type \"%s\"",
166  typeName.c_str() );
167  }
168 
169  // Return gracefully:
170  return StatusCode::SUCCESS;
171  }
172 
182  StatusCode TFileMerger::merge( EMergeMode mode, ::Long64_t entries ) {
183 
184  // Check that we have an output file:
185  if( ! m_output ) {
186  Error( "merge", XAOD_MESSAGE( "No output file specified" ) );
187  return StatusCode::FAILURE;
188  }
189 
190  // Check that we have input files:
191  if( ! m_input.size() ) {
192  Warning( "merge", "No input files were specified" );
193  return StatusCode::RECOVERABLE;
194  }
195 
196  // Check that we received consistent parameters:
197  if( ( mode == kFastMerge ) && ( entries != kBigNumber ) ) {
198  Warning( "merge", "Number of entries can't be specified in fast "
199  "merging mode" );
201  } else {
203  }
204 
205  // Merge all the input files into the output using the recursive merging
206  // function:
207  for( ::TFile* file : m_input ) {
208  RETURN_CHECK( "xAOD::TFileMerger::merge",
209  mergeDirectory( *file, *m_output, mode, true ) );
210  }
211 
212  // Make sure that everything in the output is written out:
213  m_output->SaveSelf();
214 
215  // Close all the files:
216  RETURN_CHECK( "xAOD::TFileMerger::merge", closeFiles() );
217 
218  // Return gracefully:
219  return StatusCode::SUCCESS;
220  }
221 
223 
224  return m_mode;
225  }
226 
228 
229  m_mode = mode;
230  return;
231  }
232 
233  int TFileMerger::verbosity() const {
234 
235  return m_verbosity;
236  }
237 
238  void TFileMerger::setVerbosity( int value ) {
239 
240  m_verbosity = value;
241  return;
242  }
243 
244  const std::set< std::string >& TFileMerger::treesSkipped() const {
245 
246  return m_treesToSkip;
247  }
248 
249  void TFileMerger::addTreeToSkip( const std::string& name ) {
250 
251  m_treesToSkip.insert( name );
252  return;
253  }
254 
256 
257  if( m_verbosity >= 1 ) {
258  Info( "closeFiles", "Closing all open files" );
259  }
260 
261  // A little sanity check:
262  if( m_input.size() != m_copiedInput.size() ) {
263  Fatal( "closeFiles",
264  XAOD_MESSAGE( "Internal logic error detected" ) );
265  }
266 
267  // Finish writing with the TEvent objects that are still open:
268  auto ev_itr = m_events.begin();
269  auto ev_end = m_events.end();
270  for( ; ev_itr != ev_end; ++ev_itr ) {
271  if( ev_itr->second->m_outTree && m_output ) {
272  RETURN_CHECK( "xAOD::TFileMerger::closeFiles",
273  ev_itr->second->finishWritingTo( m_output ) );
274  }
275  }
276 
277  // Close and delete the input files:
278  for( size_t i = 0; i < m_input.size(); ++i ) {
279  m_input[ i ]->Close();
280  if( m_verbosity >= 2 ) {
281  Info( "closeFiles", "Closed: %s", m_input[ i ]->GetName() );
282  }
283  if( m_copiedInput[ i ] ) {
284  TString p( m_input[ i ]->GetPath() );
285  p = p( 0, p.Index( ':', 0 ) );
286  if( gSystem->Unlink( p ) ) {
287  Error( "closeFiles",
288  XAOD_MESSAGE( "Couldn't remove local file: %s" ),
289  p.Data() );
290  return StatusCode::FAILURE;
291  }
292  if( m_verbosity >= 2 ) {
293  Info( "closeFiles", "Deleted: %s", m_input[ i ]->GetName() );
294  }
295  }
296  delete m_input[ i ];
297  }
298  m_input.clear();
299  m_copiedInput.clear();
300 
301  // Close the output file:
302  if( m_output ) {
303  m_output->Close();
304  if( m_verbosity >= 2 ) {
305  Info( "closeFiles", "Closed: %s", m_output->GetName() );
306  }
307  delete m_output;
308  m_output = 0;
309  }
310 
311  // Delete all the TEvent objects that were created during the merging:
312  m_events.clear();
313 
314  // Delete all the helper in-memory files created during the merging:
315  for( ::TFile* file : m_helperFiles ) {
316  delete file;
317  }
318  m_helperFiles.clear();
319 
320  // Return gracefully:
321  return StatusCode::SUCCESS;
322  }
323 
336  ::TDirectory& output,
337  EMergeMode mode,
338  bool topLevelDir ) {
339 
340  if( m_verbosity >= 1 ) {
341  Info( "mergeDirectory", "Merging directories:" );
342  Info( "mergeDirectory", " input : %s", input.GetName() );
343  Info( "mergeDirectory", " output: %s", output.GetName() );
344  }
345 
346  // Get a list of all objects in this directory:
347  TList* keyList = input.GetListOfKeys();
348  if( ! keyList ) {
349  Error( "mergeDirectory",
350  XAOD_MESSAGE( "Couldn't get list of keys from input directory "
351  "\"%s\"" ), input.GetName() );
352  return StatusCode::FAILURE;
353  }
354 
355  //
356  // Loop over all keys in the directory, and select the ones describing
357  // objects that we can deal with. Since one single TTree can appear
358  // multiple times in this list (with different "cycles"), keep track of
359  // which trees have already been merged into the output.
360  //
361  std::set< std::string > processedTrees;
362  for( Int_t i = 0; i < keyList->GetSize(); ++i ) {
363 
364  // Convert to a TKey:
365  TKey* key = dynamic_cast< TKey* >( keyList->At( i ) );
366  if( ! key ) {
367  Error( "mergeDirectory",
368  XAOD_MESSAGE( "Couldn't case object to TKey. No idea "
369  "why..." ) );
370  return StatusCode::FAILURE;
371  }
372  if( m_verbosity >= 3 ) {
373  Info( "mergeDirectory", "Evaluating key \"%s;%hi\" with type "
374  "\"%s\"",
375  key->GetName(), key->GetCycle(), key->GetClassName() );
376  }
377 
378  // Get the object:
379  TObject* obj = input.Get( TString::Format( "%s;%hi", key->GetName(),
380  key->GetCycle() ) );
381  if( ! obj ) {
382  Error( "mergeDirectory",
383  XAOD_MESSAGE( "Couldn't access object with name "
384  "\"%s;%hi\"" ),
385  key->GetName(), key->GetCycle() );
386  return StatusCode::FAILURE;
387  }
388 
389  //
390  // Decide how to handle this object:
391  //
392  if( obj->IsA()->InheritsFrom( "TDirectory" ) ) {
393 
394  if( m_verbosity >= 3 ) {
395  Info( "mergeDirectory", "Appears to be a TDirectory" );
396  }
397 
398  // Access the input object as a directory:
399  TDirectory* indir = dynamic_cast< TDirectory* >( obj );
400  if( ! indir ) {
401  Error( "mergeDirectory",
402  XAOD_MESSAGE( "Couldn't cast to object to TDirectory" ) );
403  return StatusCode::FAILURE;
404  }
405 
406  // Check if such a directory already exists in the output:
407  TDirectory* outdir =
408  dynamic_cast< TDirectory* >( output.Get( key->GetName() ) );
409  // If it doesn't let's create it:
410  if( ! outdir ) {
411  if( ! ( outdir = output.mkdir( key->GetName(),
412  "dummy title" ) ) ) {
413  Error( "mergeDirectory",
414  XAOD_MESSAGE( "Failed creating subdirectory with "
415  "name: %s" ),
416  key->GetName() );
417  return StatusCode::FAILURE;
418  }
419  }
420 
421  // Now call this same function recursively. Note that xAOD trees
422  // can't be in sub-directories, so the sub-directories are always
423  // merged in a fast way.
424  RETURN_CHECK( "xAOD::TFileMerger::mergeDirectory",
426  false ) );
427 
428  } else if( obj->IsA()->InheritsFrom( "TTree" ) ) {
429 
430  if( m_verbosity >= 3 ) {
431  Info( "mergeDirectory", "Appears to be a TTree" );
432  }
433 
434  // Check if this tree is to be skipped from merging. At least by
435  // this code. Because the "MetaData" tree is getting merged. Just
436  // not here...
437  if( m_treesToSkip.find( key->GetName() ) !=
438  m_treesToSkip.end() ) {
439  if( m_verbosity >= 4 ) {
440  Info( "mergeDirectory",
441  "Skipping it because of m_treesToSkip" );
442  }
443  continue;
444  }
445 
446  // Check if this tree was already processed:
447  if( processedTrees.find( key->GetName() ) !=
448  processedTrees.end() ) {
449  if( m_verbosity >= 4 ) {
450  Info( "mergeDirectory",
451  "Skipping it, because it was processed already" );
452  }
453  continue;
454  }
455 
456  if( m_verbosity >= 3 ) {
457  Info( "mergeDirectory",
458  "Tree not processed yet. Doing so now." );
459  }
460 
461  // Make sure that we have the object with the highest cycle number:
462  ::TObject* treeObj = input.Get( key->GetName() );
463  if( ! treeObj ) {
464  Fatal( "mergeDirectory",
465  XAOD_MESSAGE( "Internal logic error found" ) );
466  return StatusCode::FAILURE;
467  }
468  ::TTree* itree = dynamic_cast< TTree* >( treeObj );
469  if( ! itree ) {
470  Error( "mergeDirectory",
471  XAOD_MESSAGE( "Couldn't access \"%s\" with a TTree "
472  "pointer" ),
473  treeObj->GetName() );
474  return StatusCode::FAILURE;
475  }
476 
477  // Skip trees that have friends. Those don't play nicely with
478  // merging/cloning...
479  if( itree->GetListOfFriends() &&
480  itree->GetListOfFriends()->GetSize() ) {
481  if( m_verbosity >= 2 ) {
482  Info( "mergeDirectory", "TTree \"%s\" has friends; "
483  "skipping from merge", itree->GetName() );
484  }
485  continue;
486  }
487 
488  // Trees in the top level directory are all assumed to be xAOD
489  // trees. We need to make a TEvent object aware of this output tree
490  // regardless of the merge mode. Because it's the TEvent that will
491  // take care of merging the metadata from the files.
492  if( topLevelDir ) {
493  // Instantiate the TEvent object:
494  std::unique_ptr< TEvent >& event = m_events[ key->GetName() ];
495  if( ( ! event.get() ) || ( event->auxMode() != m_mode ) ) {
496  event.reset( new TEvent( m_mode ) );
497  event->setActive();
498  }
499  // And now create all the metadata tools:
500  RETURN_CHECK( "xAOD::TFileMerger::mergeDirectory",
502  // And now connect it to the input file:
503  RETURN_CHECK( "xAOD::TFileMerger::mergeDirectory",
504  event->readFrom( itree ) );
505  // Let the user know what's happening:
506  Info( "mergeDirectory", "Copying xAOD tree \"%s\"",
507  key->GetName() );
508  } else {
509  // Let the user know what's happening:
510  Info( "mergeDirectory", "Copying non-xAOD tree \"%s\"",
511  key->GetName() );
512  }
513 
514  // Do quite different things in fast and slow merging modes:
515  if( mode == kFastMerge ) {
516 
517  // Connect the TEvent object to the output file in order to
518  // handle metadata merging correctly:
519  if( topLevelDir ) {
520  std::unique_ptr< TEvent >& event = m_events[ key->GetName() ];
521  if( ! event.get() ) {
522  event.reset( new TEvent() );
523  }
524  if( ! event->m_outTree ) {
525  // Create an in-memory file, that's really only needed
526  // for technical reasons. Tricking the TEvent object into
527  // thinking that it's actually writing event data. While
528  // it really isn't.
529  ::TFile* ofile =
530  new ::TMemFile( TString::Format( "%sHelper.root",
531  key->GetName() ),
532  "CREATE" );
533  m_helperFiles.push_back( ofile );
534  RETURN_CHECK( "xAOD::TFileMerger::mergeDirectory",
535  event->writeTo( ofile, 200,
536  key->GetName() ) );
537  }
538  // Merge the input file's EventFormat object into the one
539  // created for the output file:
540  const EventFormat* ief = event->inputEventFormat();
541  EventFormat* oef = event->m_outputEventFormat;
542  if( ( ! ief ) || ( ! oef ) ) {
543  Error( "mergeDirectory",
544  XAOD_MESSAGE( "Internal logic error detected" ) );
545  Error( "mergeDirectory",
546  XAOD_MESSAGE( "ief = %p, oef = %p" ),
547  static_cast< const void* >( ief ),
548  static_cast< void* >( oef ) );
549  return StatusCode::FAILURE;
550  }
551  auto itr = ief->begin();
552  auto end = ief->end();
553  for( ; itr != end; ++itr ) {
554  if( ! oef->exists( itr->first ) ) {
555  oef->add( itr->second );
556  }
557  }
558  }
559 
560  //
561  // See if such a TTree exists in the output already:
562  //
563  ::TTree* otree =
564  dynamic_cast< ::TTree* >( output.Get( key->GetName() ) );
565  if( otree ) {
566 
567  if( m_verbosity >= 3 ) {
568  Info( "mergeDirectory",
569  "Merging the tree into an existing one" );
570  }
571 
572  // Check if there are any branches in either tree that don't
573  // exist in the other one:
574  const std::vector< ::TBranch* > missingMerged =
575  getMissingBranches( otree, itree );
576  const std::vector< ::TBranch* > missingIncoming =
577  getMissingBranches( itree, otree );
578  if( m_verbosity >= 4 ) {
579  Info( "mergeDirectory", "missingMerged.size() = %i",
580  static_cast< int >( missingMerged.size() ) );
581  Info( "mergeDirectory", "missingIncoming.size() = %i",
582  static_cast< int >( missingIncoming.size() ) );
583  }
584 
585  // Add branches with default values for the variables that
586  // appeared in the input tree now, and were not in the output
587  // tree yet.
588  for( ::TBranch* br : missingIncoming ) {
589  if( m_verbosity >= 4 ) {
590  Info( "mergeDirectory", "Adding auxiliary branch: %s",
591  br->GetName() );
592  }
593  RETURN_CHECK( "xAOD::TFileMerger::mergeDirectory",
594  addAuxBranch( otree, br ) );
595  }
596 
597  // Exclude branches from the incoming tree that have no
598  // dictionary in our environment:
599  const std::vector< ::TBranch* > skippedBranches =
600  getSkippedBranches( itree );
601  for( ::TBranch* br : skippedBranches ) {
602  itree->SetBranchStatus( br->GetName(), 0 );
603  if( m_verbosity >= 4 ) {
604  Info( "mergeDirectory",
605  "Deactivated branch \"%s\" from merging",
606  br->GetName() );
607  }
608  }
609 
610  // Get the indices of the aux branches in the merged tree that
611  // need to be excluded from the fast merging:
612  TObjArray* obranches = otree->GetListOfBranches();
613  std::map< ::Int_t, ::TBranch* > auxIndices;
614  for( ::TBranch* br : missingMerged ) {
615  const Int_t index = obranches->IndexOf( br );
616  if( index < 0 ) {
617  Error( "mergeDirectory",
618  XAOD_MESSAGE( "Internal logic error "
619  "detected" ) );
620  return StatusCode::FAILURE;
621  }
622  auxIndices[ index ] = br;
623  obranches->RemoveAt( index );
624  if( m_verbosity >= 4 ) {
625  Info( "mergeDirectory", "Removed branch \"%s\" from "
626  "fast merge list", br->GetName() );
627  }
628  }
629  if( auxIndices.size() ) {
630  obranches->Compress();
631  }
632 
633  // Set up a TTreeCloner object for fast-merging the branches
634  // that appear in both trees:
635  ::TTreeCloner
636  cloner( itree, otree, "fast SortBasketsByBranch",
637  ( ::TTreeCloner::kNoWarnings |
638  ::TTreeCloner::kIgnoreMissingTopLevel ) );
639  // Check if the cloner is valid:
640  if( ! cloner.IsValid()) {
641  // Let's check why
642  static const char* const okerror = "One of the export branch";
643  if( ::strncmp( cloner.GetWarning(), okerror,
644  ::strlen( okerror ) ) == 0 ) {
645  // That's fine we will handle it
646  } else {
647  Error( "mergeDirectory",
648  XAOD_MESSAGE( "Invalid TTreeCloner (%s)" ),
649  cloner.GetWarning() );
650  return StatusCode::FAILURE;
651  }
652  }
653 
654  // Run the fast merging:
655  otree->SetEntries( otree->GetEntries() +
656  itree->GetEntries() );
657  if( ! cloner.Exec() ) {
658  Error( "mergeDirectory",
659  XAOD_MESSAGE( "Failed to execute TTreeCloner" ) );
660  return StatusCode::FAILURE;
661  }
662 
663  // Put back the branches that have no counterpart in the input
664  // tree:
665  for( auto it : auxIndices ) {
666  const Int_t last = obranches->GetLast();
667  if( last >= 0 ) {
668  obranches->AddAtAndExpand( obranches->At( last ),
669  last + 1 );
670  for( Int_t ind = last - 1; ind >= it.first; --ind ) {
671  obranches->AddAt( obranches->At( ind ), ind + 1 );
672  }
673  obranches->AddAt( it.second, it.first );
674  } else {
675  obranches->Add( it.second );
676  }
677  }
678  // And fill them with default values:
679  const ::Long64_t ientries = itree->GetEntries();
680  for( ::TBranch* br : missingMerged ) {
681  br->SetAddress( 0 );
682  for( ::Long64_t i = 0; i < ientries; ++i ) {
683  if( br->Fill() < 0 ) {
684  Error( "mergeDirectory",
685  XAOD_MESSAGE( "Failed to fill branch \"%s\" "
686  "with default content" ),
687  br->GetName() );
688  return StatusCode::FAILURE;
689  }
690  }
691  }
692 
693  // Make sure that the tree is flushed:
694  otree->AutoSave( "FlushBaskets" );
695 
696  } else {
697 
698  if( m_verbosity >= 3 ) {
699  Info( "mergeDirectory", "Cloning the tree as is" );
700  }
701 
702  //
703  // If it doesn't exist, then use the TTree::CloneTree function
704  // to create a copy of the TTree in the input file. Then save
705  // this copy into the output file.
706  //
707  // But first, let's turn off all the branches that don't have
708  // a dictionary in our environment. As those will have to be
709  // skipped.
710  //
711  const std::vector< ::TBranch* > skippedBranches =
712  getSkippedBranches( itree );
713  for( ::TBranch* br : skippedBranches ) {
714  itree->SetBranchStatus( br->GetName(), 0 );
715  if( m_verbosity >= 4 ) {
716  Info( "mergeDirectory",
717  "Deactivated branch \"%s\" from cloning",
718  br->GetName() );
719  }
720  }
721 
722  // Now do the clone:
723  output.cd();
724  if( ! ( otree =
725  itree->CloneTree( -1,
726  "fast SortBasketsByBranch" ) ) ) {
727  Error( "mergeDirectory",
728  XAOD_MESSAGE( "TTree \"%s\" couldn't be cloned "
729  "into the output" ),
730  itree->GetName() );
731  return StatusCode::FAILURE;
732  }
733  otree->SetDirectory( &output );
734  otree->AutoSave();
735  }
736 
737  // Later on the metadata merging will come here.
738 
739  } else if( mode == kSlowMerge ) {
740 
741  // Access the TEvent object made for this tree. Such a thing
742  // needs to access, because we can only slow-merge top level
743  // trees. Which are always assumed to be xAOD trees.
744  std::unique_ptr< TEvent >& event = m_events[ key->GetName() ];
745  if( ! event.get() ) {
746  event.reset( new TEvent() );
747  }
748 
749  // Check whether it's already writing to the output:
750  if( ! event->m_outTree ) {
751  // Make a TFile pointer out of the TDirectory:
752  ::TFile* ofile = dynamic_cast< ::TFile* >( &output );
753  if( ! ofile ) {
754  Error( "mergeDirectory",
755  XAOD_MESSAGE( "Internal logic error found" ) );
756  return StatusCode::FAILURE;
757  }
758  RETURN_CHECK( "xAOD::TFileMerger::mergeDirectory",
759  event->writeTo( ofile, 200, key->GetName() ) );
760  }
761 
762  // Let the user know what's happening:
763  if( m_verbosity >= 3 ) {
764  Info( "mergeDirectory", "Slow copying xAOD tree \"%s\"",
765  key->GetName() );
766  }
767 
768  // Loop over the events using the TEvent object:
769  const ::Long64_t entries = event->getEntries();
770  for( ::Long64_t entry = 0; entry < entries; ++entry ) {
771 
772  // Check whether we're done already:
773  if( event->m_outTree->GetEntries() >= m_entriesToMerge ) {
774  break;
775  }
776 
777  // Load the entry:
778  if( event->getEntry( entry ) < 0 ) {
779  Error( "mergeDirectory",
780  XAOD_MESSAGE( "Couldn't get entry %i from tree "
781  "%s" ),
782  static_cast< int >( entry ), key->GetName() );
783  return StatusCode::FAILURE;
784  }
785 
786  // Give the user feedback about the processing status:
787  if( ! ( entry % 500 ) ) {
788  Info( "mergeDirectory", "Copied %i / %i entries",
789  static_cast< int >( entry ),
791  static_cast< int >( entries ) :
792  static_cast< int >( m_entriesToMerge ) ) );
793  }
794 
795  // Copy the full event to the output:
796  RETURN_CHECK( "xAOD::TFileMerger::mergeDirectory",
797  event->copy() );
798 
799  // Write the event:
800  const ::Int_t bytes = event->fill();
801  if( bytes < 0 ) {
802  Error( "mergeDirectory",
803  XAOD_MESSAGE( "There was an error in writing entry "
804  "%i from tree %s" ),
805  static_cast< int >( entry ), key->GetName() );
806  return StatusCode::FAILURE;
807  } else if( bytes == 0 ) {
808  Warning( "mergeDirectory",
809  "No payload was written for entry %i",
810  static_cast< int >( entry ) );
811  }
812  }
813 
814  } else {
815  Fatal( "mergeDirectory",
816  XAOD_MESSAGE( "Unknown mode (%i) received" ),
817  static_cast< int >( mode ) );
818  }
819 
820  // Remember that this TTree has already been processed:
821  processedTrees.insert( key->GetName() );
822 
823  } else if( obj->IsA()->InheritsFrom( "TObject" ) ) {
824 
825  if( m_verbosity >= 3 ) {
826  Info( "mergeDirectory", "Appears to be a TObject of type %s",
827  obj->IsA()->GetName() );
828  }
829 
830  // Skip objects that we don't have a dictionary for:
831  ::TClass* cl = ::TClass::GetClass( key->GetClassName() );
832  if( ( ! cl ) || ( ! cl->IsLoaded() ) ) {
833  continue;
834  }
835 
836  if( m_verbosity >= 3 ) {
837  Info( "mergeDirectory", "We seem to have a dictionary for it" );
838  }
839 
840  // Check if the object is already in the output:
841  TObject* oobj = output.Get( key->GetName() );
842 
843  // If the output object already exists, and we can merge this input
844  // object into it, then that's all that we need to do:
845  if( oobj ) {
846  const StatusCode ret = mergeObject( *obj, *oobj );
847  if( ret.isSuccess() ) {
848  // Make sure that the output object is updated in the output
849  // file:
850  oobj->Write( 0, TObject::kOverwrite );
851  continue;
852  } else if( ret.isFailure() ) {
853  // This is not good:
854  Error( "mergeDirectory",
855  XAOD_MESSAGE( "Error detected while merging object "
856  "\"%s\"" ), obj->GetName() );
857  return StatusCode::FAILURE;
858  }
859  }
860 
861  if( m_verbosity >= 3 ) {
862  Info( "mergeDirectory", "Simply writing it to the output" );
863  }
864 
865  // If the object doesn't exist yet, or there's no merge operation
866  // defined for it, just write this object to the output as is:
867  output.cd();
868  obj->Write();
869  }
870  }
871 
872  // Return gracefully:
873  return StatusCode::SUCCESS;
874  }
875 
892  StatusCode TFileMerger::mergeObject( ::TObject& input, ::TObject& output ) {
893 
894  if( m_verbosity >= 3 ) {
895  Info( "mergeObject", "Called mergeObject( %s, %s )",
896  input.GetName(), output.GetName() );
897  }
898 
899  // A little sanity check:
900  if( strcmp( input.IsA()->GetName(), output.IsA()->GetName() ) ) {
901  Error( "mergeObject",
902  XAOD_MESSAGE( "Received objects of different types" ) );
903  Error( "mergeObject",
904  XAOD_MESSAGE( "input = %s" ), input.IsA()->GetName() );
905  Error( "mergeObject",
906  XAOD_MESSAGE( "output = %s" ), output.IsA()->GetName() );
907  return StatusCode::FAILURE;
908  }
909 
910  // Check if there is a merge function defined for these objects:
911  ::TMethodCall mergeMethod;
912  mergeMethod.InitWithPrototype( output.IsA(), "Merge", "TCollection*" );
913  if( ! mergeMethod.IsValid() ) {
914  // No, there isn't:
915  if( m_verbosity >= 3 ) {
916  Info( "mergeObject", "Type doesn't have a Merge function" );
917  }
918  return StatusCode::RECOVERABLE;
919  }
920 
921  // Put the input object into a list, since that's what we need to give
922  // to the merge function.
923  ::TList list;
924  list.Add( &input );
925 
926  // Execute the merging:
927  mergeMethod.SetParam( ( Long_t ) &list );
928  mergeMethod.Execute( &output );
929 
930  if( m_verbosity >= 3 ) {
931  Info( "mergeObject", "Merging executed successfully" );
932  }
933 
934  // Return gracefully:
935  return StatusCode::SUCCESS;
936  }
937 
939 
940  // Create all the specified tool types:
941  for( const std::string& typeName : m_metaDataToolNames ) {
942 
943  // If the tool was already created, don't do it again:
944  if( m_metaDataTools.find( typeName ) != m_metaDataTools.end() ) {
945  continue;
946  }
947 
948  // Try to create an instance of the tool:
949  ::TClass* cl = ::TClass::GetClass( typeName.c_str() );
950  if( ! cl ) {
951  Error( "createMetaDataTools",
952  XAOD_MESSAGE( "Tool of type \"%s\" not found" ),
953  typeName.c_str() );
954  return StatusCode::FAILURE;
955  }
956  void* ptr = cl->New();
957  if( ! ptr ) {
958  Error( "createMetaDataTools",
959  XAOD_MESSAGE( "Failed to create an instance of tool "
960  "\"%s\"" ), typeName.c_str() );
961  return StatusCode::FAILURE;
962  }
963 
964  // Try to call the tool's initialize() function:
965  ::TMethodCall initMethod;
966  initMethod.InitWithPrototype( cl, "initialize", "" );
967  if( initMethod.IsValid() ) {
968  // Check the return type of the initialize() function. If it's a
969  // class that has an ignore() function, then let's call that.
970  const std::string returnTypeName =
971  initMethod.GetMethod()->GetReturnTypeNormalizedName();
972  ::TClass* returnCl = ::TClass::GetClass( returnTypeName.c_str(),
973  kTRUE, kTRUE );
974  if( returnCl &&
975  returnCl->GetListOfMethods()->FindObject( "ignore" ) ) {
976  char* statusCode = 0;
977  initMethod.Execute( ptr, "", &statusCode );
978  if( ! statusCode ) {
979  Warning( "createMetaDataTools", "No StatusCode was returned" );
980  }
981  ::TMethodCall ignoreMethod;
982  ignoreMethod.InitWithPrototype( returnCl, "ignore", "" );
983  if( ignoreMethod.IsValid() && statusCode ) {
984  ignoreMethod.Execute( static_cast< void* >( statusCode ) );
985  } else {
986  Error( "createMetaDataTools",
987  XAOD_MESSAGE( "Failed to access the ignore() function "
988  "of the initialize() function's return "
989  "type" ) );
990  }
991  } else {
992  // If not, then let's just ignore the return value of the
993  // function:
994 
995  initMethod.Execute( ptr );
996  }
997  } else {
998  Warning( "createMetaDataTools",
999  "No initialize() function found for tool \"%s\"",
1000  typeName.c_str() );
1001  }
1002 
1003  // Store the pointer of this object:
1004  m_metaDataTools[ typeName ] = THolder( ptr, cl );
1005  if( m_verbosity > 0 ) {
1006  Info( "createMetaDataTools", "Created tool of type \"%s\"",
1007  typeName.c_str() );
1008  }
1009  }
1010 
1011  // Return gracefully:
1012  return StatusCode::SUCCESS;
1013  }
1014 
1023  std::vector< ::TBranch* >
1025  ::TTree* second ) const {
1026 
1027  // The result object:
1028  std::vector< ::TBranch* > result;
1029 
1030  // List of branches from the two trees:
1031  ::TObjArray* brFirst = first->GetListOfBranches();
1032  ::TObjArray* brSecond = second->GetListOfBranches();
1033 
1034  // Loop over the branches of the first tree:
1035  for( ::Int_t i = 0; i < brFirst->GetEntries(); ++i ) {
1036  // Check if such a branch exists in the second tree:
1037  if( brSecond->FindObject( brFirst->At( i )->GetName() ) ) {
1038  continue;
1039  }
1040  // Access the branch:
1041  ::TBranch* br = dynamic_cast< ::TBranch* >( brFirst->At( i ) );
1042  if( ! br ) {
1043  Fatal( "getMissingBranches",
1044  XAOD_MESSAGE( "Couldn't cast branch to TBranch?!?" ) );
1045  return result;
1046  }
1047  // Skip it if we don't have a dictionary for it:
1048  ::TClass* cl = 0;
1049  ::EDataType dt = kOther_t;
1050  if( br->GetExpectedType( cl, dt ) ) {
1051  Error( "getMissingBranches",
1052  XAOD_MESSAGE( "Type could not be extracted for branch "
1053  "\"%s\"" ), br->GetName() );
1054  continue;
1055  }
1056  if( cl && ( ! cl->IsLoaded() ) ) {
1057  continue;
1058  }
1059  // Okay, this is apparently a branch that we'll be able to handle
1060  // correctly.
1061  result.push_back( br );
1062  }
1063 
1064  // Return the collected branches:
1065  return result;
1066  }
1067 
1068  std::vector< ::TBranch* >
1069  TFileMerger::getSkippedBranches( ::TTree* tree ) const {
1070 
1071  // The result object:
1072  std::vector< ::TBranch* > result;
1073 
1074  // The list of all branches from the tree:
1075  ::TObjArray* branches = tree->GetListOfBranches();
1076 
1077  // Select the ones that we don't have a dictionary for. These will not
1078  // be cloned.
1079  for( ::Int_t i = 0; i < branches->GetEntries(); ++i ) {
1080  // The branch:
1081  ::TBranch* br = dynamic_cast< ::TBranch* >( branches->At( i ) );
1082  if( ! br ) {
1083  Fatal( "getSkippedBranches",
1084  XAOD_MESSAGE( "Couldn't cast branch to TBranch?!?" ) );
1085  return result;
1086  }
1087  // Check the type of it:
1088  ::TClass* cl = 0;
1089  ::EDataType dt = kOther_t;
1090  if( br->GetExpectedType( cl, dt ) ) {
1091  Error( "getSkippedBranches",
1092  XAOD_MESSAGE( "Type could not be extracted for branch "
1093  "\"%s\"" ), br->GetName() );
1094  continue;
1095  }
1096  // Skip the branches that come from a class that doesn't have a
1097  // compiled dictionary:
1098  if( cl && ( ! cl->IsLoaded() ) ) {
1099  result.push_back( br );
1100  }
1101  }
1102 
1103  return result;
1104  }
1105 
1113  StatusCode TFileMerger::addAuxBranch( ::TTree* otree,
1114  ::TBranch* ibranch ) const {
1115 
1116  // Get the type of the branch:
1117  ::TClass* cl = 0;
1118  ::EDataType dt = kOther_t;
1119  if( ibranch->GetExpectedType( cl, dt ) ) {
1120  Error( "addAuxBranch",
1121  XAOD_MESSAGE( "Type could not be extracted for branch \"%s\"" ),
1122  ibranch->GetName() );
1123  return StatusCode::FAILURE;
1124  }
1125 
1126  // Pointer to the output branch:
1127  ::TBranch* obranch = 0;
1128 
1129  // Decide what sort of branch it is:
1130  if( cl ) {
1131  // It's an object type:
1132  void* ptr = 0;
1133  obranch = otree->Branch( ibranch->GetName(), cl->GetName(),
1134  &ptr, ibranch->GetBasketSize(),
1135  ibranch->GetSplitLevel() );
1136  if( ! obranch ) {
1137  Error( "addAuxBranch",
1138  XAOD_MESSAGE( "Couldn't create auxiliary branch \"%s\" with "
1139  "type: %s" ),
1140  ibranch->GetName(), cl->GetName() );
1141  return StatusCode::FAILURE;
1142  }
1143  } else if( dt != kOther_t ) {
1144  // It's a primitive type:
1145  const char rootType =
1147  if( rootType == '\0' ) {
1148  Error( "addAuxBranch",
1149  XAOD_MESSAGE( "Type can't be extracted for EDataType = %i"),
1150  static_cast< int >( dt ) );
1151  return StatusCode::FAILURE;
1152  }
1153  std::ostringstream leaflist;
1154  leaflist << ibranch->GetName() << "/" << rootType;
1155  void* ptr = 0;
1156  obranch = otree->Branch( ibranch->GetName(), ptr,
1157  leaflist.str().c_str(),
1158  ibranch->GetBasketSize() );
1159  if( ! obranch ) {
1160  Error( "addAuxBranch",
1161  XAOD_MESSAGE( "Couldn't create auxiliary branch \"%s\" with "
1162  "data type: %i" ),
1163  ibranch->GetName(), static_cast< int >( dt ) );
1164  return StatusCode::FAILURE;
1165  }
1166  } else {
1167  Error( "addAuxBranch",
1168  XAOD_MESSAGE( "Couldn't figure out the type of branch \"%s\"" ),
1169  ibranch->GetName() );
1170  return StatusCode::FAILURE;
1171  }
1172 
1173  // Fill up the branch with dummy entries:
1174  obranch->SetAddress( 0 );
1175  for( ::Long64_t i = 0; i < otree->GetEntries(); ++i ) {
1176  if( obranch->Fill() < 0 ) {
1177  Error( "addAuxBranch",
1178  XAOD_MESSAGE( "Failed to fill branch \"%s\" with dummy "
1179  "data" ), obranch->GetName() );
1180  return StatusCode::FAILURE;
1181  }
1182  }
1183 
1184  // Return gracefully:
1185  return StatusCode::SUCCESS;
1186  }
1187 
1188 } // namespace xAOD
xAOD::TFileMerger::m_verbosity
int m_verbosity
Verbosity level.
Definition: TFileMerger.h:143
TFileMerger.h
xAOD::TFileMerger::merge
StatusCode merge(EMergeMode mode=kFastMerge, ::Long64_t entries=kBigNumber)
Execute the file merge itself.
python.SystemOfUnits.second
int second
Definition: SystemOfUnits.py:120
xAOD::TFileMerger::verbosity
int verbosity() const
The current verbosity level.
RETURN_CHECK
#define RETURN_CHECK(CONTEXT, EXP)
Helper macro for checking return codes in a compact form in the code.
Definition: ReturnCheck.h:26
xAOD::TFileMerger::m_helperFiles
std::vector< ::TFile * > m_helperFiles
Helper in-memory files used during fast merging.
Definition: TFileMerger.h:129
xAOD::name
name
Definition: TriggerMenuJson_v1.cxx:29
get_generator_info.result
result
Definition: get_generator_info.py:21
index
Definition: index.py:1
StateLessPT_NewConfig.Format
Format
Definition: StateLessPT_NewConfig.py:146
tree
TChain * tree
Definition: tile_monitor.h:30
xAOD::TFileMerger::setVerbosity
void setVerbosity(int value)
Set verbosity level (meant for debugging mainly)
validation.ofile
ofile
Definition: validation.py:96
xAOD::TFileMerger::addFile
StatusCode addFile(const std::string &name, bool copyLocally=false)
Add a file to the list to be merged, by name.
skel.it
it
Definition: skel.GENtoEVGEN.py:396
ClassImp
ClassImp(xAOD::TFileMerger) namespace xAOD
Implement the auto-generated functions of the class.
Definition: TFileMerger.cxx:35
xAOD::TFileMerger::m_mode
TEvent::EAuxMode m_mode
Access mode for the created TEvent objects.
Definition: TFileMerger.h:137
athena.value
value
Definition: athena.py:124
xAOD
ICaloAffectedTool is abstract interface for tools checking if 4 mom is in calo affected region.
Definition: ICaloAffectedTool.h:24
XAOD_MESSAGE
#define XAOD_MESSAGE(MESSAGE)
Simple macro for printing error/verbose messages.
Definition: Control/xAODRootAccess/xAODRootAccess/tools/Message.h:19
Utils.h
dbg::ptr
void * ptr(T *p)
Definition: SGImplSvc.cxx:74
plotting.yearwise_efficiency.indir
indir
Definition: yearwise_efficiency.py:25
xAOD::TEvent::kClassAccess
@ kClassAccess
Access auxiliary data using the aux containers.
Definition: Control/xAODRootAccess/xAODRootAccess/TEvent.h:100
dumpTruth.statusCode
statusCode
Definition: dumpTruth.py:85
xAOD::TFileMerger::closeFiles
StatusCode closeFiles()
Close all the open files.
xAOD::TFileMerger::createMetaDataTools
StatusCode createMetaDataTools()
Instantiate the metadata handling tools.
ReturnCheck.h
xAOD::TFileMerger::m_copiedInput
std::vector< bool > m_copiedInput
Whether the files were copied locally before being merged.
Definition: TFileMerger.h:119
mergePhysValFiles.end
end
Definition: DataQuality/DataQualityUtils/scripts/mergePhysValFiles.py:93
plotting.efficiency.outdir
outdir
Definition: efficiency.py:18
runLayerRecalibration.branches
list branches
Definition: runLayerRecalibration.py:98
xAOD::TFileMerger::addAuxBranch
StatusCode addAuxBranch(::TTree *otree, ::TBranch *ibranch) const
Duplicate an auxiliary branch from the input into the output.
python.utils.AtlRunQueryDQUtils.p
p
Definition: AtlRunQueryDQUtils.py:210
POOL::TEvent::readFrom
StatusCode readFrom(TFile *file)
Definition: PhysicsAnalysis/POOLRootAccess/src/TEvent.cxx:133
xAOD::TFileMerger::kSlowMerge
@ kSlowMerge
Merging is done using TEvent.
Definition: TFileMerger.h:72
xAOD::TFileMerger::addMetaDataTool
StatusCode addMetaDataTool(const std::string &typeName)
Add a metadata tool to be used during the merging.
xAOD::TFileMerger::m_metaDataTools
std::map< std::string, THolder > m_metaDataTools
Metadata tools instantiated for the file merging.
Definition: TFileMerger.h:134
xAOD::TFileMerger::m_treesToSkip
std::set< std::string > m_treesToSkip
Names of the TTrees to skip from the merge.
Definition: TFileMerger.h:124
event
POOL::TEvent event(POOL::TEvent::kClassAccess)
POOL::TEvent::getEntry
int getEntry(long entry)
Definition: PhysicsAnalysis/POOLRootAccess/src/TEvent.cxx:185
xAOD::TFileMerger::getSkippedBranches
std::vector< ::TBranch * > getSkippedBranches(::TTree *tree) const
Get the branches that should be skipped from merging.
lumiFormat.i
int i
Definition: lumiFormat.py:85
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
Message.h
CaloNoise_fillDB.dt
dt
Definition: CaloNoise_fillDB.py:58
xAOD::TFileMerger::~TFileMerger
~TFileMerger()
Destructor.
xAOD::TFileMerger::TFileMerger
TFileMerger()
Default constructor.
PlotPulseshapeFromCool.input
input
Definition: PlotPulseshapeFromCool.py:106
xAOD::TFileMerger::accessMode
TEvent::EAuxMode accessMode() const
The access mode used for slow and metadata merging.
file
TFile * file
Definition: tile_monitor.h:29
Preparation.mode
mode
Definition: Preparation.py:94
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
merge.output
output
Definition: merge.py:17
xAOD::TFileMerger::m_output
::TFile * m_output
The output file to write to.
Definition: TFileMerger.h:121
GetAllXsec.entry
list entry
Definition: GetAllXsec.py:132
xAOD::TFileMerger::m_metaDataToolNames
std::set< std::string > m_metaDataToolNames
Names of the metadata handling tools.
Definition: TFileMerger.h:132
xAOD::TFileMerger::setOutputFileName
StatusCode setOutputFileName(const std::string &name, const std::string &mode="RECREATE")
Set the name of the output file that should be created.
xAOD::TFileMerger
Helper class for merging xAOD files.
Definition: TFileMerger.h:47
xAOD::Utils::getTypeInfo
const std::type_info & getTypeInfo(EDataType type)
This function is used when reading a primitive branch from an input file without the user explicitly ...
Definition: Control/xAODRootAccess/Root/Utils.cxx:169
xAOD::TFileMerger::kFastMerge
@ kFastMerge
Merging is done using fast TTree merge.
Definition: TFileMerger.h:73
DeMoScan.index
string index
Definition: DeMoScan.py:364
xAOD::TFileMerger::treesSkipped
const std::set< std::string > & treesSkipped() const
Get the names of the trees that will be skipped from the merge.
xAOD::TEvent::EAuxMode
EAuxMode
Auxiliary store "mode".
Definition: Control/xAODRootAccess/xAODRootAccess/TEvent.h:98
xAOD::TFileMerger::m_input
std::vector< ::TFile * > m_input
The list of input files to be merged.
Definition: TFileMerger.h:117
xAOD::TFileMerger::setAccessMode
void setAccessMode(TEvent::EAuxMode mode)
Set the access mode used for slow and metadata merging.
DeMoScan.first
bool first
Definition: DeMoScan.py:536
xAOD::EventFormat
EventFormat_v1 EventFormat
Definition of the current event format version.
Definition: EventFormat.h:16
entries
double entries
Definition: listroot.cxx:49
ReadCalibFromCool.typeName
typeName
Definition: ReadCalibFromCool.py:477
LArCellNtuple.ifile
string ifile
Definition: LArCellNtuple.py:133
xAOD::TFileMerger::m_entriesToMerge
::Long64_t m_entriesToMerge
Events to merge into the output.
Definition: TFileMerger.h:140
xAOD::TFileMerger::kBigNumber
static const ::Long64_t kBigNumber
Number of entries that are assumed to be never exceeded in a file.
Definition: TFileMerger.h:77
python.PyAthena.obj
obj
Definition: PyAthena.py:132
xAOD::TFileMerger::mergeDirectory
StatusCode mergeDirectory(::TDirectory &input, ::TDirectory &output, EMergeMode mode, bool topLevelDir)
Merge the contents of one directory from the input files.
dq_make_web_display.cl
cl
print [x.__class__ for x in toList(dqregion.getSubRegions()) ]
Definition: dq_make_web_display.py:26
xAOD::TFileMerger::addTreeToSkip
void addTreeToSkip(const std::string &name)
Add a tree name that should be skipped during the merging.
xAOD::TFileMerger::mergeObject
StatusCode mergeObject(::TObject &input, ::TObject &output)
Merge two top level objects that were found in the inputs.
xAOD::Utils::rootType
char rootType(char typeidType)
This function is used internally in the code when creating primitive dynamic auxiliary branches.
Definition: Control/xAODRootAccess/Root/Utils.cxx:226
xAOD::TFileMerger::m_events
std::map< std::string, std::unique_ptr< TEvent > > m_events
Helper objects for merging the xAOD Tree(s) from the input file(s)
Definition: TFileMerger.h:127
checkFileSG.ind
list ind
Definition: checkFileSG.py:118
PlotCalibFromCool.br
br
Definition: PlotCalibFromCool.py:355
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37
xAOD::TFileMerger::getMissingBranches
std::vector< ::TBranch * > getMissingBranches(::TTree *first, ::TTree *second) const
Get the auxiliary branches missing in one of the trees.