ATLAS Offline Software
Functions
TFileMerger.cxx File Reference
#include <cstring>
#include <set>
#include <sstream>
#include <TFile.h>
#include <TFileMergeInfo.h>
#include <TMemFile.h>
#include <TDirectory.h>
#include <TUUID.h>
#include <TSystem.h>
#include <TString.h>
#include <TList.h>
#include <TMethodCall.h>
#include <TClass.h>
#include <TKey.h>
#include <TTree.h>
#include <TROOT.h>
#include <TFunction.h>
#include <TTreeCloner.h>
#include "xAODRootAccess/tools/TFileMerger.h"
#include "xAODRootAccess/tools/Message.h"
#include "xAODRootAccess/tools/ReturnCheck.h"
#include "xAODRootAccess/tools/Utils.h"

Go to the source code of this file.

Functions

 ClassImp (xAOD::TFileMerger) namespace xAOD
 Implement the auto-generated functions of the class. More...
 

Function Documentation

◆ ClassImp()

ClassImp ( xAOD::TFileMerger  )

Implement the auto-generated functions of the class.

In order to merge various kinds of metadata from the input files correctly into the output file, it is possible to specify the type names of dual-use metadata handling tools. The object then creates an instance of the specified tool, which should take care of merging its metadata type in the background.

Parameters
typeNameThe type name of the tool to create
Returns
StatusCode::SUCCESS if the tool was successfully created, StatusCode::FAILURE if the tool could not be created, and StatusCode::RECOVERABLE if a tool of this type is already held by the object

This is the main function of the class. It executes the merging of the input xAOD files in the specified mode, and then closes all the files.

Parameters
modeThe merging mode to be used
entriesEntries to be merged into the output file. Only effective in slow merging mode.
Returns
StatusCode::SUCCESS if the merging was successful, or StatusCode::FAILURE if it wasn't

This function does most of the heavy-lifting in the code. It recursively merges all the objects from an input directory into the output file's corresponding directory.

Parameters
inputDirectory in one of the input files
outputThe corresponding directory in the output file
modeThe merging mode for the xAOD trees
topLevelDirFlag whether this is the top directory in the file(s)
Returns
StatusCode::SUCCESS if the merging succeeded, or StatusCode::FAILURE if it didn't

This function is used internally to merge two generic ROOT objects in the file together. It's not the most trivial, since TObject doesn't have a merge function itself.

The function fails with StatusCode::RECOVERABLE when encountering types that don't have a Merge(...) function defined. In this case we need to save both objects, with different versions, into the output.

Parameters
inputThe input object
outputThe output object
Returns
StatusCode::Success if the merging succeeded, StatusCode::FAILURE if there was a technical issue, and StatusCode::RECOVERABLE if the objects don't define a merging function.

This function finds branches that appear in the first tree, but don't appear in the second one.

Parameters
firstThe "first" tree
secondThe "second" tree
Returns
The branches that appear in the "first" tree, but don't appear in the "second" one

This function is used during fast merging to create branches that have been encountered first in file N (N >= 2).

Parameters
otreeThe output tree to create the branch in
ibranchPointer to the branch in the input tree
Returns
The usual StatusCode types

Definition at line 35 of file TFileMerger.cxx.

37  {
38 
39  TFileMerger::TFileMerger()
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 
54  TFileMerger::~TFileMerger() {
55 
56  // Close all the files that may still be open:
57  closeFiles().ignore();
58  }
59 
60  StatusCode TFileMerger::setOutputFileName( const std::string& name,
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" );
200  m_entriesToMerge = kBigNumber;
201  } else {
202  m_entriesToMerge = entries;
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 
222  TEvent::EAuxMode TFileMerger::accessMode() const {
223 
224  return m_mode;
225  }
226 
227  void TFileMerger::setAccessMode( TEvent::EAuxMode mode ) {
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 
255  StatusCode TFileMerger::closeFiles() {
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 
335  StatusCode TFileMerger::mergeDirectory( ::TDirectory& input,
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",
425  mergeDirectory( *indir, *outdir, kFastMerge,
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",
501  createMetaDataTools() );
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 ),
790  ( m_entriesToMerge == kBigNumber ?
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 
938  StatusCode TFileMerger::createMetaDataTools() {
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* >
1024  TFileMerger::getMissingBranches( ::TTree* first,
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
TestSUSYToolsAlg.ifile
ifile
Definition: TestSUSYToolsAlg.py:92
python.SystemOfUnits.second
int second
Definition: SystemOfUnits.py:120
RETURN_CHECK
#define RETURN_CHECK(CONTEXT, EXP)
Helper macro for checking return codes in a compact form in the code.
Definition: ReturnCheck.h:26
get_generator_info.result
result
Definition: get_generator_info.py:21
python.PerfMonSerializer.p
def p
Definition: PerfMonSerializer.py:743
index
Definition: index.py:1
StateLessPT_NewConfig.Format
Format
Definition: StateLessPT_NewConfig.py:146
tree
TChain * tree
Definition: tile_monitor.h:30
validation.ofile
ofile
Definition: validation.py:96
skel.it
it
Definition: skel.GENtoEVGEN.py:423
athena.value
value
Definition: athena.py:122
XAOD_MESSAGE
#define XAOD_MESSAGE(MESSAGE)
Simple macro for printing error/verbose messages.
Definition: Control/xAODRootAccess/xAODRootAccess/tools/Message.h:19
plotting.yearwise_efficiency.indir
indir
Definition: yearwise_efficiency.py:29
dumpTruth.statusCode
statusCode
Definition: dumpTruth.py:89
ZDCMsg::Info
@ Info
Definition: ZDCMsg.h:20
mergePhysValFiles.end
end
Definition: DataQuality/DataQualityUtils/scripts/mergePhysValFiles.py:93
plotting.efficiency.outdir
outdir
Definition: efficiency.py:19
runLayerRecalibration.branches
list branches
Definition: runLayerRecalibration.py:98
POOL::TEvent::readFrom
StatusCode readFrom(TFile *file)
Definition: PhysicsAnalysis/POOLRootAccess/src/TEvent.cxx:132
event
POOL::TEvent event(POOL::TEvent::kClassAccess)
POOL::TEvent::getEntry
int getEntry(long entry)
Definition: PhysicsAnalysis/POOLRootAccess/src/TEvent.cxx:184
lumiFormat.i
int i
Definition: lumiFormat.py:92
ret
T ret(T t)
Definition: rootspy.cxx:260
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
CaloNoise_fillDB.dt
dt
Definition: CaloNoise_fillDB.py:58
PlotPulseshapeFromCool.input
input
Definition: PlotPulseshapeFromCool.py:106
file
TFile * file
Definition: tile_monitor.h:29
Preparation.mode
mode
Definition: Preparation.py:95
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
merge.output
output
Definition: merge.py:17
GetAllXsec.entry
list entry
Definition: GetAllXsec.py:132
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:195
covarianceTool.verbosity
verbosity
Definition: covarianceTool.py:513
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
DeMoScan.index
string index
Definition: DeMoScan.py:362
DeMoScan.first
bool first
Definition: DeMoScan.py:534
xAOD::EventFormat
EventFormat_v1 EventFormat
Definition of the current event format version.
Definition: EventFormat.h:16
pool::accessMode
const char * accessMode(pool::DbAccessMode access_mode)
Translate access mode to string.
python.utility.LHE.merge
def merge(input_file_pattern, output_file)
Merge many input LHE files into a single output file.
Definition: LHE.py:17
entries
double entries
Definition: listroot.cxx:49
ReadCalibFromCool.typeName
typeName
Definition: ReadCalibFromCool.py:477
ZDCMsg::Fatal
@ Fatal
Definition: ZDCMsg.h:23
L1Topo::Error
Error
The different types of error that can be flagged in the L1TopoRDO.
Definition: Error.h:16
python.PyAthena.obj
obj
Definition: PyAthena.py:135
dq_make_web_display.cl
cl
print [x.__class__ for x in toList(dqregion.getSubRegions()) ]
Definition: dq_make_web_display.py:26
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
checkFileSG.ind
list ind
Definition: checkFileSG.py:118
PlotCalibFromCool.br
br
Definition: PlotCalibFromCool.py:355
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37