/* This macro will add histograms multiplied by their cross-section from a list of root files and write them to a target root file. The target file is newly created and must not be identical to one of the source files. Author: Sven A. Schmidt, sven.schmidt@cern.ch Date: 13.2.2001 Editing Author: Michael B. Anderson, mbanderson@hep.wisc.edu Date: July 12, 2007 This code is based on the hadd.C example by Rene Brun and Dirk Geppert, which had a problem with directories more than one level deep. (see macro hadd_old.C for this previous implementation). The macro from Sven has been enhanced by Anne-Sylvie Nicollerat to automatically add Trees (via a chain of trees). To use this macro, modify the file names in function hadd. NB: This macro is provided as a tutorial. Use $ROOTSYS/bin/hadd to merge many histogram files */ #include #include "TChain.h" #include "TFile.h" #include "TH1.h" #include "TTree.h" #include "TKey.h" #include "Riostream.h" TList *FileList; TFile *Target; void MergeRootfile( TDirectory *target, TList *sourcelist, double crossArray[] ); void MergingWithProperWeighting(TString fInputDirectory) { TString outputDir = "mergedAnalysisFiles"; gSystem->Exec("mkdir "+outputDir); Target = TFile::Open( Form("%s/All.root",outputDir.Data()), "RECREATE" ); FileList = new TList(); // ************************************************************ // List of Files FileList->Add( TFile::Open(Form("%s/FileA.root",fInputDirectory.Data()))); FileList->Add( TFile::Open(Form("%s/FileB.root",fInputDirectory.Data()))); // List of Cross-sections divided by num of events produced // -------------------------------------------------------- double crossSections[2] = { 1.2,1.}; // ************************************************************ cout << "going to call merging routine..." << endl; MergeRecursive( Target, FileList, 0 ,crossSections ); cout << "done." << endl; } Bool_t MergeRecursive(TDirectory *target, TList *sourcelist, Int_t isdir, double crossArray[]) { // Merge all objects in a directory // NB. This function is a copy of the hadd function MergeROOTFile //cout << "Target path: " << target->GetPath() << endl; TString path( (char*)strstr( target->GetPath(), ":" ) ); path.Remove( 0, 2 ); //gain time, do not add the objects in the list in memory Bool_t addDirStat = TH1::AddDirectoryStatus(); TH1::AddDirectory(kFALSE); TDirectory *first_source = (TDirectory*)sourcelist->First(); THashList allNames; while (first_source) { TDirectory *current_sourcedir = first_source->GetDirectory(path); if (!current_sourcedir) { first_source = (TDirectory*)sourcelist->After(first_source); continue; } // loop over all keys in this directory TChain *globChain = 0; TIter nextkey( current_sourcedir->GetListOfKeys() ); TKey *key, *oldkey=0; while ( (key = (TKey*)nextkey())) { if (current_sourcedir == target) break; //keep only the highest cycle number for each key if (oldkey && !strcmp(oldkey->GetName(),key->GetName())) continue; if (!strcmp(key->GetClassName(),"TProcessID")) {key->ReadObj(); continue;} if (allNames.FindObject(key->GetName())) continue; allNames.Add(new TObjString(key->GetName())); // read object from first source file current_sourcedir->cd(); TObject *obj = key->ReadObj(); if ( obj->IsA()->InheritsFrom( "TH1" ) ) { // descendant of TH1 -> merge it TH1 *h1 = (TH1*)obj; h1.Scale(crossArray[0]); h1.Sumw2(); TList listH; // loop over all source files and add the content of the // correspondant histogram to the one pointed to by "h1" TFile *nextsource = (TFile*)sourcelist->After( first_source ); int q = 1; // This keeps track of which // cross section factor to use while ( nextsource ) { // make sure we are at the correct directory level by cd'ing to path TDirectory *ndir = nextsource->GetDirectory(path); if (ndir) { ndir->cd(); TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName()); if (key2) { TObject *hobj = key2->ReadObj(); hobj->ResetBit(kMustCleanup); listH.Add(hobj); hobj->Scale(crossArray[q]); h1->Add( h2 ); q++; listH.Delete(); } } nextsource = (TFile*)sourcelist->After( nextsource ); } } else if ( obj->IsA()->InheritsFrom( "TTree" ) ) { // loop over all source files create a chain of Trees "globChain" if (!fNoTrees) { TString obj_name; if (path.Length()) { obj_name = path + "/" + obj->GetName(); } else { obj_name = obj->GetName(); } globChain = new TChain(obj_name); globChain->Add(first_source->GetName()); TFile *nextsource = (TFile*)sourcelist->After( first_source ); while ( nextsource ) { //do not add to the list a file that does not contain this Tree TFile *curf = TFile::Open(nextsource->GetName()); if (curf) { Bool_t mustAdd = kFALSE; if (curf->FindKey(obj_name)) { mustAdd = kTRUE; } else { //we could be more clever here. No need to import the object //we are missing a function in TDirectory TObject *aobj = curf->Get(obj_name); if (aobj) { mustAdd = kTRUE; delete aobj;} } if (mustAdd) { globChain->Add(nextsource->GetName()); } } delete curf; nextsource = (TFile*)sourcelist->After( nextsource ); } } } else if ( obj->IsA()->InheritsFrom( "TDirectory" ) ) { // it's a subdirectory //cout << "Found subdirectory " << obj->GetName() << endl; // create a new subdir of same name and title in the target file target->cd(); TDirectory *newdir = target->mkdir( obj->GetName(), obj->GetTitle() ); // newdir is now the starting point of another round of merging // newdir still knows its depth within the target file via // GetPath(), so we can still figure out where we are in the recursion MergeRecursive( newdir, sourcelist,1); } else { TMethodCall callEnv; if (obj->IsA()) callEnv.InitWithPrototype(obj->IsA(), "Merge", "TCollection*"); if (callEnv.IsValid()) { TList* tomerge = new TList; TFile *nextsource = (TFile*)sourcelist->After(first_source); while (nextsource) { nextsource->cd(path); TObject *newobj = gDirectory->Get(obj->GetName()); if (newobj) { tomerge->Add(newobj); } nextsource = (TFile*)sourcelist->After(nextsource); } callEnv.SetParam((Long_t) tomerge); callEnv.Execute(obj); delete tomerge; } else { target->cd(); obj->Write(); TFile *nextsource = (TFile*)sourcelist->After(first_source); while (nextsource) { nextsource->cd(path); TObject *newobj = gDirectory->Get(obj->GetName()); if (newobj) { target->cd(); newobj->Write(); } nextsource = (TFile*)sourcelist->After(nextsource); } Warning("MergeRecursive", "object type without Merge function will be added unmerged, name: %s title: %s", obj->GetName(), obj->GetTitle()); TH1::AddDirectory(addDirStat); return kTRUE; } } // now write the merged histogram (which is "in" obj) to the target file // note that this will just store obj in the current directory level, // which is not persistent until the complete directory itself is stored // by "target->Write()" below if ( obj ) { target->cd(); //!!if the object is a tree, it is stored in globChain... if(obj->IsA()->InheritsFrom( "TDirectory" )) { //printf("cas d'une directory\n"); } else if(obj->IsA()->InheritsFrom( "TTree" )) { if (!fNoTrees) { globChain->ls(); if (fFastMethod) globChain->Merge(target->GetFile(),0,"keep fast"); else globChain->Merge(target->GetFile(),0,"keep"); delete globChain; } } else if (obj->IsA()->InheritsFrom( "TCollection" )) { obj->Write( key->GetName(), TObject::kSingleKey ); } else { obj->Write( key->GetName() ); } } oldkey = key; } // while ( ( TKey *key = (TKey*)nextkey() ) ) first_source = (TDirectory*)sourcelist->After(first_source); } // save modifications to target file target->SaveSelf(kTRUE); if (!isdir) sourcelist->Remove(sourcelist->First()); TH1::AddDirectory(addDirStat); return kTRUE; }