Back Midas Rome Roody Rootana
  Midas DAQ System  Not logged in ELOG logo
Entry  17 Oct 2007, Randolf Pohl, Forum, Adding MIDAS .root-files 
    Reply  17 Oct 2007, John M O'Donnell, Forum, Adding MIDAS .root-files histoAdd.cxx
Message ID: 415     Entry time: 17 Oct 2007     In reply to: 412
Author: John M O'Donnell 
Topic: Forum 
Subject: Adding MIDAS .root-files 
The following program handles regular directories in a file, or folders (ugh).
Most histograms are added bin by bin.

For scaler events it is convenient to see the counts as a function of time (ala
sclaer history plots in mhttpd).  If the histogram looks like a scaler plot versus
time, then new bins are added on to the end (or into the middle!) of the histogram.

All different versions of cuts are kept.

TTrees are not explicitly supported, so probably don't do the right thing...

John.

> Dear MIDAS users,
> 
> I want to add several .root-files produced by the MIDAS analyzer, in a fast 
> and convenient way. ROOT's hadd fails because it does not know how to treat 
> TFolders. I guess this problem is not unique to me, so I hope that somebody of 
> you might already have found a solution.
> 
> Why don't I just run "analyzer -r 1 10000"?
> We have taken lots of runs under (rapidly) varying conditions, so it would be 
> lots of "-r". And the analysis is quite involved, so rerunning all data takes 
> about one hour on a fast PC making this quite painful.
> Therefore, I would like to rerun all data only once, and then add the result 
> files depending on different criteria.
> 
> Of course, I tried to write a script that does the adding. But somehow it is 
> incredibly slow. And I am not the Master Of C++, too.
> 
> Is there any deeper reason for MIDAS using TFolders, not TDirectorys? ROOT's 
> hadd can treat TDirectory. Can I simply patch "my" MIDAS? Is there general 
> interest in a change like this? (Does anyone have experience with the speed of 
> hadd?)
> 
> Looking forward to comments from the Forum.
> 
> Cheers,
> 
> Randolf
Attachment 1: histoAdd.cxx  10 kB  | Hide | Hide all
#include <iostream>
#include <vector>
#include <iterator>
#include <cstring>
using namespace std;

#include "TROOT.h"
#include "TFile.h"
#include "TString.h"
#include "TDirectory.h"
#include "TObject.h"
#include "TClass.h"
#include "TKey.h"
#include "TH1.h"
#include "TAxis.h"
#include "TMath.h"
#include "TCutG.h"
#include "TFolder.h"

bool verbose (false);
TFolder *histosFolder (0);

//==============================================================================

void addObject (TObject *o, const TString &prefix, TFolder *opFolder=0);

/** called for each file to do the addition.
  * loops over each object in the file, and
  * uses addObject to dispatch the actuall addition.
  */

void add (TFile *file, const TString &prefix) {

//------------------------------------------------------------------------------

  TString dirName (file->GetName());
  if (verbose) cout << " scanning TFile" << endl;
  TString newPrefix (prefix + dirName + "/");

  TIter i (file->GetListOfKeys());
  while (TKey *k = static_cast<TKey *>( i())) {

    TObject *o (file->Get( k->GetName()));
    addObject( o, newPrefix);
  }

  return;
}

//==============================================================================

/** Most histograms are added bin by bin, but if simpleAdd == false,
  * the xaxis values are assumed different, in which case we look up
  * appropriate bin numbers, and use a new extended xaxis if needed.
  *
  * Use simpleAdd=false to accumulate scaler rate histograms.
  */
void add (const TH1 *newh, TH1 *&hsum, TFolder *opFolder) {

//------------------------------------------------------------------------------

  const bool simpleAdd (newh->GetXaxis()->GetTimeDisplay() ? false : true);

  if (!hsum) {
    hsum = (TH1 *)newh->Clone();
    TString title = "histoAdd: ";
    title += hsum->GetTitle();
    if (opFolder) hsum->SetDirectory( 0);
  }
  else if (simpleAdd) hsum->Add( newh);
  else { // extend axis - for 1D histos with equal sized bins

    size_t nBinsSum = hsum->GetNbinsX();
    size_t nBinsNew = newh->GetNbinsX();
    vector<Double_t>bin_contents;
    vector<Double_t>histo_edges;
    Int_t holder_bins;

    /* foundBin is either the overflow bin (if the 2 histograms don't overlap)
     * or it is the bin number in histoA that has the same low edge as the 
     * lowest bin edge in histoB. The only time that histograms can overlap 
     * is when the older scaler.cxx is used to create the histograms with 
     * fixed bin sizes. 
     */
    Int_t foundBin = hsum->FindBin( newh->GetBinLowEdge(1) );

    histo_edges.resize(foundBin);
    bin_contents.resize(foundBin);
    
    for( int i = 1; i <= foundBin; i++ ) {
      histo_edges[i-1] = hsum->GetBinLowEdge(i);
      bin_contents[i-1] = hsum->GetBinContent(i);
    }

    if(foundBin < nBinsSum)  { 
      //the histos overlap or we have already made holder bins
      holder_bins = 0;
    }
    else        {
      //create a "place holder" histo
      Int_t width = 10;
      holder_bins = (int)((newh->GetXaxis()->GetXmin() 
                    - hsum->GetXaxis()->GetXmax())/width);

      if( holder_bins < width ) holder_bins = width;

      TH1F *bin_holder = new TH1F("bin_holder", "bin_holder", holder_bins, 
                           hsum->GetXaxis()->GetXmax(), 
                           newh->GetXaxis()->GetXmin() );

      histo_edges.resize( foundBin + holder_bins );
      bin_contents.resize( foundBin + holder_bins );

      for( int i = 0; i < holder_bins; i++ )      {
        histo_edges[foundBin+i] = bin_holder->GetBinLowEdge(i+2);
        bin_contents[foundBin+i] = 0;
      }
      delete bin_holder;
    } //end else
  
    histo_edges.resize(  foundBin + holder_bins + nBinsNew+1 ); 
    bin_contents.resize( foundBin + holder_bins + nBinsNew+1 );

    for( int i = 0; i <= nBinsNew; i++ )   {
      histo_edges[i+foundBin+holder_bins] = newh->GetBinLowEdge(i+1);
      bin_contents[i+foundBin+holder_bins] = newh->GetBinContent(i+1);
    }

    hsum->SetBins( histo_edges.size()-1, &histo_edges[0] );

    for ( int i=1; i<histo_edges.size(); ++i) {
      hsum->SetBinContent( i, bin_contents[i-1]);
    }

    if (opFolder) {
      //opFolder->Remove( hsum);
      //opFolder->Add( exth);
      hsum->SetDirectory( 0);
    }
  }

  if (verbose) {
    if (simpleAdd) cout << " adding counts";
    else cout << " tagging on bins";
    cout << endl;
  }

  return;
}

//==============================================================================

/** Most cuts are written out just once, but if a cut is different from
  * the most recently written version of a cut with the same name, then
  * another copy of the cut is written out.  Thus if a cut changes during
  * a series of runs, all versions of the cut will be present in the
  * summed file.
  */
void add (const TCutG *o, TCutG *&oOut, TFolder *opFolder) {

//------------------------------------------------------------------------------

  const char *name (o->GetName());
  bool write (false);

  if (!oOut) write = true;
  else {

    Int_t n (o->GetN());
    if (n != oOut->GetN()) write = true;

    else {
      double x1, x2, y1, y2;
      for (Int_t i=0; i<n; ++i) {

        o   ->GetPoint( i, x1, y1);
        oOut->GetPoint( i, x2, y2);

        if ((x1 != x2) || (y1 != y2)) write = true;
      }
    }
  }

  if (write) {

    if (verbose) {
      if (oOut) cout << " changed";
      cout << " TCutG" << endl;
    }

    if (!opFolder) {

      oOut = const_cast<TCutG *>( o);
      o->Write( name, TObject::kSingleKey);

    } else {

      TCutG *clone = static_cast<TCutG *>( o->Clone());
      if (oOut) opFolder->Add( clone);
      oOut = clone;
    }
  } else if (verbose) cout << endl;

  return;
}

//==============================================================================

/** for most objects, we keep just the first version.
  */
void add (const TObject *o, TObject *&oSum, TFolder *opFolder) {

//------------------------------------------------------------------------------

  const char *name (o->GetName());

  if (!oSum) {
    if (verbose) cout << " saving TObject" << endl;
    if (!opFolder) o->Write( name, TObject::kSingleKey);
  } else {
    if (verbose) cout << endl;
  }

  return;
}

//==============================================================================

/** create the new directory and then start adding its contents
  */
void add (TDirectory *dir, TDirectory *&sumDir,
          TFolder *opFolder, const TString &prefix) {

//------------------------------------------------------------------------------

  TDirectory *currentDir (gDirectory);
  TString dirName (dir->GetName());
  if (verbose) cout << " scanning TDirectory" << endl;
  TString newPrefix (prefix + dirName + "/");

  if (!sumDir) sumDir = gDirectory->mkdir( dirName);
  sumDir->cd();

  TIter i (dir->GetListOfKeys());
  while (TKey *k = static_cast<TKey *>( i())) {

    TObject *o (dir->Get( k->GetName()));
    addObject( o, newPrefix, opFolder);
  }

  currentDir->cd();

  return;
}

//==============================================================================

/** create a new folder and then start adding its contents
  */
void add (const TFolder *folder, TFolder *&sumFolder,
          TFolder *parentFolder, const TString &prefix) {

//------------------------------------------------------------------------------

  if (verbose) cout << " scanning TFolder" << endl;
  const char *name (folder->GetName());
  TString newPrefix (prefix + name + "/");

  if (!sumFolder) sumFolder = new TFolder (name, name);
  if (!histosFolder) histosFolder = sumFolder;

  TIter i (folder->GetListOfFolders());
  while (TObject *o = i()) addObject( o, newPrefix, sumFolder);

  return;
}

//==============================================================================

int main (int argc, char **argv) {

//------------------------------------------------------------------------------

  if (argc < 3) {
    cerr << argv[0] << ": out_root_file in_root_file1 in_root_file2 ..." << endl;
    return 1;
  }

  TROOT root ("histoadd", "histoadd");
  root.SetBatch();

  TString opFileName (argv[1]);
  TFile *opFile (TFile::Open( opFileName, "RECREATE"));

  if (!opFile) {
    cerr << argv[0] << ": unable to open file: " << argv[1] << endl;
    return 1;
  }
  --argc;
  ++argv;
... 95 more lines ...
ELOG V3.1.4-2e1708b5