Making a skeleton class for reading condensed data files

I’m attempting to write a program, using the root libraries, to do some analysis using “condensed” data files that a colleague has created.
He created these files on a 32 bit machine, using a class that he defined in-line in a root script, e.g.

class condTrk{
public:
   
  //leaves of branch condTrk:
  
  //fheader
  Int_t run;
  Int_t subrun;
  Int_t runtype;

...
};

So, when I put this in my program (or CINT script, either way it fails) and try to read tracks out of the file like so:

TChain *chain = new TChain("condNtpSR"); 
chain->Add("/data/CondNtp.2006-07-sun.root");
condTrk *track = new condTrk();
chain->SetBranchAddress("condensed", &track);
chain->GetEntry(0);
cout << "Run: " << track->run << " Subrun: " << track->subrun << endl;

I get a segfault on my 64-bit system, but not on my 32-bit system, when the GetEntry happens, I believe. I really can’t see where there is any architecture-dependent things going on here.

I attempted to use TTree.MakeClass() to make a separate class file from the root files I am reading, but that resulted in me getting gibberish in the read back variables.

So, maybe I could use some instruction in making a skeleton class, or maybe it’s just not possible to read the 32-bit generated files on a 64-bit system, but I would appreciate some help.[/code]

Hi,

Did you compile (and generate a dictionary for) condTrk?
How did your colleague define the branch?

Philippe.

My particular example here does not have a compiled condTrk.
The condTrk definition I put inside the main .cxx file. (What seems to be quite a kludge, if you ask me, but it made it work.)

The way these root files were generated was essentially via a root script that looked at larger files and just filled the things of interest.

Here’s an outline/excerpt of the condensed file generating script:

class condTrk
{
public:
   
  //leaves of branch condTrk:
  
  //fheader
  Int_t run;    //filled from standard ntuple variable
  Int_t subrun; //filled from standard ntuple variable
  ....and so on through about 100 variables
};

void cond_ntuples_sun(){
 ... much setup code...
 TFile *file1 = new TFile(Path,"NEW"); 
 TTree *condNtpSR=(TTree*)gDirectory->FindObject("condNtpSR");  
    if (condNtpSR){
      delete condNtpSR;
      condNtpSR=0; 
    }
    condNtpSR = new TTree("condNtpSR","condNtpSR");
    
    //create branch      
    
    condTrk *condensed = new condTrk;
    condNtpSR -> Branch("condensed", &condensed);    

    for each event in the larger file{
	  condensed->run = myreco->GetHeader().GetRun();
	  condensed->subrun = myreco->GetHeader().GetSubRun();//fSubRun;

 ... the code continues filling the other variables here
      condNtpSR->Fill();
    }
}

This was all in one .C script, so there never really was a formal class definition file (or dictionary, I think) for condTrk at any stage. I’m not sure if I’m describing the problem adequately.

What I tried in my initial post was to put the class definition inline with my script (and I also tried it inline with a program simply compiled with ROOT libraries.)

I had also tried using TTree.MakeClass() to make a .C and .h pair from the files I had been given, but as I said, this gave me nonsense values when I tried to use it.

Humm … Unless you and your colleague are compiling your script (via ACLiC for example), which would solve your problem for sure, you are relying on the unsupported I/O of interpreted classes which is known to have some issues …

Cheers,
Philippe.

He did use ACLiC for his macro.

And I have been working primarily with compiled code.

What’s the “right” way to do this? We can regenerate the files and go to just a minimal trouble, I suppose.

I basically just need a skeleton class to be able to write into a file.

Thanks for all your help.

Hi,

A priori all the code you send should work in compiled mode (providing that you either compile with ACLiC or generate the dictionary for the condTrk class).
If that is what you do and it is not yet working please send me a complete example on how to reproduce your failure.

Cheers,
Philippe.

I think you’re probably right, that some stage of this is being interpreted incorrectly.

I’d like to try making the class in the proper way, with a cxx and a h file, first, and then use those in the file generation and reading stages and see if that works.

I’m trying to figure out the simplest possible class to do this with. Is it something very close to what MakeClass() generated?

MakeClass ‘removes’ your object model into simple ints and floats, in your case this might be sufficient though. Take a look at the output of MakeClass (or better the output of MakeSelector).

Philippe

Plain ints and doubles are sufficient for my needs.

I just made a very simple test case root files with and without aclic.

I’ve attached the generating script.
To do it without aclic, I did .x make_files.C
To do it with aclic, I did .x make_files.C++

When opening the root file in a regular root session that was generated with aclic, this happens:

Attaching file testfile.aclic.root as _file0...
Warning in <TClass::TClass>: no dictionary for class condTrk is available

If I understand you right, this should NOT happen, aclic should make the dictionary.
make_files.C (1 KB)

[quote]If I understand you right, this should NOT happen, aclic should make the dictionary.[/quote]Yes it should since you have not loaded the library (and hence the dictionary) in your read session.

Cheers,
Philippe

I was under the impression that root files were self-documented.

Okay, so I took the testfile.root file that I generated with that script that I posted (via .x make_files.C++) and did:

root [0] TFile* f = new TFile("testfile.root")
Warning in <TClass::TClass>: no dictionary for class condTrk is available
root [1] f->ls()
TFile**         testfile.root
 TFile*         testfile.root
  KEY: TTree    condNtpSR;2     condNtpSR
  KEY: TTree    condNtpSR;1     condNtpSR
root [2] condNtpSR.MakeClass("condTrk")
Info in <TTreePlayer::MakeClass>: Files: condTrk.h and condTrk.C generated from TTree: condNtpSR
(Int_t)0
root[3] .q

(Question here too, why are there two condNtpSR TTrees? Have I mistakenly written twice in my file above?)
Now, in a new root session:

root [0] .L condTrk.C+
root [1] condTrk* track = new condTrk()

At this point root exits with no message or anything. I don’t think it’s supposed to do this. This makes no sense to me. Is this a bug or am I misusing the tools supplied?

If I try in a slightly different way:

root [0] TFile* f = new TFile("testfile.root")
Warning in <TClass::TClass>: no dictionary for class condTrk is available
root [1] condNtpSR.MakeClass()
Info in <TTreePlayer::MakeClass>: Files: condNtpSR.h and condNtpSR.C generated from TTree: condNtpSR
(Int_t)0
root [2] .q

Then

root [0] .L condNtpSR.C+
Info in <TUnixSystem::ACLiC>: creating shared library /home/bbock/cond_example/./condNtpSR_C.so
In file included from /home/bbock/cond_example/./condNtpSR.C:2,
                 from /home/bbock/cond_example/./filedQ5yE7.h:32,
                 from /home/bbock/cond_example/./filedQ5yE7.cxx:16:
/home/bbock/cond_example/./condNtpSR.h:134: warning: unused parameter 'entry'
root [1] condNtpSR* track = new condNtpSR()
Warning in <TClass::TClass>: no dictionary for class condTrk is available
root [2]                                                                                

So, I haven’t rectified the dictionary error in this case! Am I just doing something ridiculously stupid?

Actually, I have realized that when the condTrk tries to construct itself in the default way that MakeClass() sets it up to be, it actually tries to load itself from the chain. So I believe root gets in a self referential loop and just decides to exit.

My solution to this was to strip the class down to the bare bones, and then it started working, and I can use it successfully in the interpreter to read these test files.

Now, I would like to use it in a compiled program. This seems much more difficult. I am trying to follow the example in $ROOTSYS/test/
Specifically, the Event test that is there.
But, oddly enough, at this late hour, I think I have made it work, finally. One might find the example slightly less confusing if the executable generated did not have the same name as the class getting used, forcing this strange division between EVENT and MAINEVENT in the makefile. But that’s just a documentation nitpick.

Thanks for all the help, I"ll post again if I run into more difficulty.

[quote]I was under the impression that root files were self-documented. [/quote]There are :slight_smile:. The nomenclature is here a bit confusing (too many things can be called ‘dictionary’).
When I used the word ‘dictionary’ in this post, I refered to the entity that described a C++ compiled class whose shared library has been loaded.

[quote](Question here too, why are there two condNtpSR TTrees? Have I mistakenly written twice in my file above?) [/quote]The ‘;1’ is a ‘backup’ copy of the TTree object (we call those ‘cycles’). See the User’s Guide for more details.

HUmm … In your example you have your data class named ‘condTrk’ and you r wrapper class (the result of MakeClass) which is also name condTrk. This confused the system (because the result of MakeClass does not have the same content nor the same layout as your original data class).

By calling MakeClass without a name you get a default name that is different than your data class so it works …

[quote]So, I haven’t rectified the dictionary error in this case! Am I just doing something ridiculously stupid?[/quote]As I mentioned, the result of MakeClass does NOT use your original data object and thus it is okay to not have a ‘dictionary’ for condTrk (the self describing information in the file and the content of the result of MakeClass are sufficient).

Cheers,
Philippe.