ATLAS Offline Software
Loading...
Searching...
No Matches
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.

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