How to organize data for hundreds of histograms with root

Dear friends of root,

I wrote a module for the belle2 analysis framework that takes our trackobjecs and calculates dozens or even hundreds of different quality indicates from them. (mainly layer wise pulls and chi^2 of the fitted track parameters)
At the moment I use the boost statistic lib to calculate the means and standard deviations which works fine.

Now I also want to write write the data itself into a file with for further processing mailing to plot histograms from it.

I thought about it for a wile and talked to others in my institute that use root mainly for data analysis but did not come up with a good idea so far.

Two features I want to have seem to make it difficult: The number of datasets I want to store is only known at runtime and it sould be easy to add more datasets in the code in the future.

For example to save the pulls of the 5 helix track parameters at every detector layer into a tree I tried this:
Write a class with a vector<vector> as an attribute and a default constructor that resizes this vector<vector> a n x m matrix.
The I create a tree with a branch using that class.
I can fill that tree with e.g 100 objects (all with the same n and m of course) of my class but then I do not know how to access the data.
Something naive like mytree->Draw(“B1.a[0][0]”) dit not work. If I use a TMatrixD instead of a std::vector all data entries in one matrix a treated as if they belong to one data sample which is not what I want.

I also thought about an TNTuple but then I would need more then 100 branches and the manual discourages that.

So what I want at the end is a file that includes named and sorted data samples (name like pulls_from_forward_updated_mometum and sorted like layer 1,2,3,4… )

Thanks in advance for any comments, tips, suggestions

kind regards

Moritz Nadler

Maybe I should add some code to make if more clear what I am talking about:

At the moment I have these containers in my program (StatisticsContainer is a typedef to a boost class)
std::map<std::string, StatisticsContainer > m_trackWiseDataSamples;
std::map<std::string, std::vector > m_trackWiseDataVecSamples;
std::map<std::string, std::vector<std::vector > > m_layerWiseDataSamples;
when I calculate something like the total chi^2 of the track it goes into the m_trackWiseDataSamples map with the marked as “totalChi2” . When I calculate the pulls of the momentum Cartesian components of the track at its extrapolated origin it goes into m_trackWiseDataVecSamples unter the name lets say “vertexMom” and so on.

Now is it possible to recreate datastructures like this in root?

[quote]mytree->Draw(“B1.a[0][0]”) dit not work.[/quote]How/why did it fail?

It sounds like you could create one branch for each of the ‘index’ value of the map (the string) and containing
the related object. I.e. for ‘totalChi2’, a StatisticsContainer. for ‘vertexMom’, a std::vector. (But I am not quite sure I understood correctly the cardinality / dimensions of your problems (i.e. what is an ‘entry’ in you case)).

Cheers,
Philippe.

Thanks for your reply

It just opens an an plain white empty window.

Interestingly it works using
mytree->Draw(“B1.getij(1,1)”);
Then an actual histogram with 100 zeros appears in the window.
This is what I expext with this test code:

class myHistoBuilder {
public:	
	myHistoBuilder(){
		int n = 2;
		int m = 2;
		zs_and_chi2_fu_t.resize(n);
		for( int i = 0; i != n; ++i){
			zs_and_chi2_fu_t[i].resize(m);			
		}
	}
	float getij(int i, int j){
		return zs_and_chi2_fu_t[i][j];
	}
	vector<vector<float> > zs_and_chi2_fu_t;
	ClassDef(myHistoBuilder, 1);
};
int testMyHistoMaker(){
	myHistoBuilder* aPtr = new myHistoBuilder();
	cout << aPtr->zs_and_chi2_fu_t[0][0] << "\n"; // test if default constructor worked
	TFile myTFile( "testOutput.root", "RECREATE");
	TTree* myTTree = new TTree("myTTree", "aTree");
	myTTree->Bronch("B1","myHistoBuilder",&aPtr);
	for( int i = 0; i != 100; ++i ){
		myTTree->Fill();
	}
	myTTree->Write();
	return 0;
}
}

What I do not know to do is filling the attribute of the class with different values.
I tried to add
aPtr->zs_and_chi2_fu_t[1][1] = 2;
in the loop before the Fill() but after that there still were only 100 zeros in the histogram created with:

	TFile* myfile = TFile::Open("testOutput.root");
	TTree* mytree = (TTree*)gDirectory->Get("myTTree");
	mytree->Draw("B1.getij(1,1)");

Does anybody know how to do it right?

Thanks in advance and kind regards

Moritz Nadler

Hi Moritz,

The most likely cause is that you interpreter the class myHistoBuilder rather than compiling it. Storing of interpreted class is not supported. To solve the problem, you should try to compile the script via ACliC.

Cheers,
Philippe.

Your suggestion seems to have solved the problem, thanks!

By the way is the statement
ClassDef(myHistoBuilder, 1);
needed?

And can it’s omitted cause problems like this:
Error in TTree::Bronch: Cannot find class:Belle2::tracking::TrackWiseDataStruct
althogh I did inlcude this header:

#ifndef TrackWiseDataStruct_H #define TrackWiseDataStruct_H namespace Belle2 { namespace tracking{ struct TrackWiseDataStruct{ public: TrackWiseDataStruct(){ data = 0.0f; } float data; }; } } #endif

kind regards

Moritz Nadler

[quote]By the way is the statement
ClassDef(myHistoBuilder, 1);
needed?[/quote]It is not required unless you inherit (directly or indirectly) from TObject, however it is useful for performance and clarity.

[quote]Error in TTree::Bronch: Cannot find class:Belle2::tracking::TrackWiseDataStruct
althogh I did inlcude this header:
[/quote]This indicates that the dictionary was not generated. Either load that file separately via ACLiC or add #ifdef __MAKECINT__ #pragma link namespace Belle2; #pragma link namespace Belle2::tracking; #pragma link class Belle2::tracking::TrackWiseDataStruct+; #endif;

Cheers,
Philippe.