A problem writing histograms to a file

Post your fixed source code.

Also, say I want to include the file names in a config file (“in” format), and read them to the program, how would you do that? I’m confused as to how you do that.

Say I create the file with the names of my files, and then I read it like so:

TEnv *input_file = new TEnv("config_file");
input_file->ReadFile("names.in", kEnvChange);

How do I loop over the file names?

Sure it’s really the same:


void histo_norm_2(){

  vector<string> input_vector;
  string input_file;
  std::cout << "Name the files to read (When you're finished, enter *):" << std::endl;

  while(input_file != "*"){
    std::cin >> input_file;
    input_vector.push_back(input_file);

    if (input_file == "*") break;

  }


  for(int n = 0 ; input_vector.size() > n ; n++){
  TFile *output = TFile::Open(TString::Format("output_%s", (input_vector[n]).c_str()), "RECREATE");
  std::cout << "Opening " << output->GetName() << std::endl;   
  TFile *input_file = TFile::Open(input_vector[n].c_str());
    //f->ls();
    TDirectory *input_dir = gDirectory;

    TIter next_dir(input_dir->GetListOfKeys());
    TKey *dir_key;

    while((dir_key = (TKey*) next_dir())){
      if(dir_key->IsFolder()){                                     
        input_file->cd();
        input_dir->cd(dir_key->GetName());                           
        //input_file->cd();
        TIter next_folder(gDirectory->GetListOfKeys());             
        TKey *key_folder;

        while((key_folder = (TKey*) next_folder())){ 
          TClass *cl = gROOT->GetClass(key_folder->GetClassName());  
          if(!cl->InheritsFrom("TH1")) continue;                     
          TH1 *h = (TH1*)key_folder->ReadObj();
	  if (h) {
	    //h->SetDirectory(output);
	    std::cout << "Connecting " << h->GetName() << std::endl;
	    Normalize(h , 1.0);
	  }
        }
      }
    }

    delete input_file;
    output->Write();
    std::cout << "Closing " << output->GetName() << std::endl;
    delete output;
  }
}

Make sure that you uncomment the “SetDirectory” line.

The “Closing” must appear for every “n”.
Try to comment out the “Normalize” line.

I commented the normalizing line, It actually closed.

Still empty though.

About what you way, I don’t know if that line is the problem, as there is only histograms on those
folders. It’s not writing them to the file (still)… I could make another case, keeping the file structure.

As for this question : A problem writing histograms to a file , should I make another topic?

So, it seems that your “Normalize” is the problem.

Make sure that you uncomment the “SetDirectory” line, otherwise the “output” file will be empty.

This is my Normalize:

void Normalize (TH1 *h1, double scale){


  Double_t ScaleFactor = 1.0;                     
  if(h1->Integral() != 0) {                         
    ScaleFactor = scale/(h1->Integral());          
  }
  else{
    std::cout << h1->GetName() << "Non-normalizable" << std::endl; 
    exit(1);                                                      
  }
  h1->Scale(ScaleFactor);                                          
}

Also, I commented those lines and this is my output:

root [0] .x histo_norm_2.cc 
Name the files to read (When you're finished, enter *):
ww_nocut-1.root
*
Opening output_ww_nocut-1.root
Connecting Nevents
Connecting nJets
Connecting nTaus
//Histogram names
Closing output_ww_nocut-1.root
Opening output_* //these are errors
Error in <TFile::TFile>: file * does not exist
Closing output_*



Still, the output file is empty. This is just routine by now.

If you still have problems, attach your “ww_nocut-1.root” file here for inspection.

#include "TH1.h"
#include "TFile.h"
#include "TDirectory.h"
#include "TCollection.h"
#include "TKey.h"
#include "TClass.h"
#include "TROOT.h"

#include <iostream>
#include <string>
#include <vector>

void Normalize(TH1 *h, Double_t norm) {
  if ((!h) || (norm == 0.)) return; // just a precaution
  Double_t integral = h->Integral();
  if (integral != 0.) h->Scale(norm / integral);
  else std::cout << h->GetName() << " is non-normalizable." << std::endl;
}

void histo_norm() {
  std::vector<std::string> input_vector;
  std::string input_string;
  
  std::cout << "Name the files to read (When you're finished, enter *):" << std::endl;
  
  while(std::cin >> input_string) {
    if (input_string == "*") break;
    input_vector.push_back(input_string);
  }
  
  for(unsigned int n = 0; n < input_vector.size(); n++) {
    TFile *output = TFile::Open(TString::Format("output_%s", (input_vector[n]).c_str()), "RECREATE");
    std::cout << "Opening " << output->GetName() << std::endl;
    
    TFile *input_file = TFile::Open((input_vector[n]).c_str());
    //f->ls();
    TDirectory *input_dir = gDirectory;
    
    TIter next_dir(input_dir->GetListOfKeys());
    TKey *dir_key;
    
    while((dir_key = (TKey*) next_dir())){
      if(dir_key->IsFolder()){ // Warning: TTree and TNtuple are also "folders"
        input_file->cd();
        input_dir->cd(dir_key->GetName());
        //input_file->cd();
        TIter next_folder(gDirectory->GetListOfKeys());
        TKey *key_folder;
	
        while((key_folder = (TKey*) next_folder())){
          TClass *cl = gROOT->GetClass(key_folder->GetClassName());
          if(!cl->InheritsFrom("TH1")) continue;
          TH1 *h = (TH1*)key_folder->ReadObj();
	  if (h) {
	    h->SetDirectory(output);
	    std::cout << "Connecting " << h->GetName() << std::endl;
	    Normalize(h, 1.0);
	  }
        }
      }
    }
    
    delete input_file;
    
    output->Write();
    std::cout << "Closing " << output->GetName() << std::endl;
    delete output;
  }
}
1 Like

Thank you very much! It works now. Now I only need to keep the folder structure of the file, but I can do that.

Have a nice day!

FYI, here is how you’d do it in Go (with groot):

package main

import (
	"flag"
	"log"

	"github.com/pkg/errors"
	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/rhist"
	"go-hep.org/x/hep/groot/riofs"
	"go-hep.org/x/hep/groot/root"
	"go-hep.org/x/hep/hbook/rootcnv"
)

func main() {
	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
		log.Fatalf("missing path(s) to file(s) to process")
	}

	for _, fname := range flag.Args() {
		err := process(fname)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func process(fname string) error {
	f, err := groot.Open(fname)
	if err != nil {
		return errors.Wrapf(err, "could not open ROOT file")
	}
	defer f.Close()

	fnew, err := groot.Create(fname + ".new")
	if err != nil {
		return errors.Wrapf(err, "could not create ROOT file")
	}
	defer fnew.Close()

	err = riofs.Walk(f, func(path string, obj root.Object, err error) error {
		name := path[len(f.Name()):]
		switch obj := obj.(type) {
		case *riofs.File:
			// no op
			return err
		case riofs.Directory:
			_, err = riofs.Dir(fnew).Mkdir(name)
			if err != nil {
				return errors.Wrapf(err, "could not create directory %q", path)
			}
			return err
		case rhist.H1:
			h, err := rootcnv.H1D(obj)
			if err != nil {
				return errors.Wrapf(err, "could not convert TH1 %q to hbook.H1", obj.Name())
			}
			h.Scale(1 / h.Integral())

			// put normalized histo to the new ROOT file.
			err = riofs.Dir(fnew).Put(name, rootcnv.FromH1D(h))
			if err != nil {
				return errors.Wrapf(err, "could not store normalized TH1 %q to %q", obj.Name(), path)
			}
			return err

		default:
			err = riofs.Dir(fnew).Put(name, obj)
			if err != nil {
				return errors.Wrapf(err, "could not store object (type=%T) to %q", obj, path)
			}
		}
		return err
	})

	if err != nil {
		return errors.Wrapf(err, "could not traverse ROOT file")
	}

	err = fnew.Close()
	if err != nil {
		return errors.Wrapf(err, "could not save ROOT file")
	}

	return nil
}

and here is an example:

$> root-ls ./histos.root
=== [./histos.root] ===
version: 61400
TDirectoryFile   dir1    dir1    (cycle=1)
  TDirectoryFile dir11   dir11   (cycle=1)
    TH1D         h1      h1      (cycle=1)
TDirectoryFile dir2    dir2    (cycle=1)
TDirectoryFile dir3    dir3    (cycle=1)

$> go run ./main.go ./histos.root
$> root-ls ./histos.new
root-ls ./histos.root.new 
=== [./histos.root.new] ===
version: 61600
TDirectoryFile   dir1    dir1    (cycle=1)
  TDirectoryFile dir11   dir11   (cycle=1)
    TH1D         h1      h1      (cycle=1)
TDirectoryFile dir2    dir2    (cycle=1)
TDirectoryFile dir3    dir3    (cycle=1)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.