Several questions concerning tree

Dear Rooters,

please find attached a small testprogram. It is loosly based on the jets.c tutorial.
It creates an Event which contains serveral channel classes (MyChannel). These channels contain a Signaltrace, which is created with a signaltrace creator (MySignalTrace). To only record data of interest from this Signaltrace there is a class that suppresses the “zeros” (MyZeroSuppressor). This class produces several pulses, wich are subsets of the original trace and are stored in the class MyPuls. This class basicially holds a pointer to the first Point of interest of the original Signaltrace and a length. These properties are stored in a TArrayC.
The MyChannel class has a container wich stores the puls classes (std::vector<MyPuls*> pulses) . If there are more pulses than the container can take, this container is increased.
To visualize wether the Signaltrace creator and the Zerosuppressor are working correctly there is a class MyEventViewer. It opens a canvas and shows the zerosuppressed traces of the Channel.

I want to store the events in a Roottree. And I have not succeeded to go to the readRootFile part of the macro.

There are serveral questions that I have:

1.) The script fails, when calling T->Fill(). I could trace that back to the using of TArrayC. Is there anything wrong in the way I am using this class? Or is this not the reason why it throws a c++ exeption?

2.) Usually a Channel has less Pulses than the container provides. The correct number is stored in MyChannel::nbrPulses. How can I tell the tree not to store the whole container, but only the contents that are necessary?

3.) Instead of using a std::vector is it better to have a TClonesArray container? Can I tell the TClonesArray Container wich contents to write to the tree?

4.) Right now everything is done with ACLIC. Can I also do
the same thing in a Visual Studio 2003 Project? What besides creating a dictionary do I have to do to make it run, once its running with ACLIC?

5.) There are values in MyEvent and MyChannel that are not going to change for each following event. Can those values be stored in some kind of tree header as they will just increase the memory that is needed for each event?

ROOT Ver. 5.18
OS: WinXp Prof.

Thank you,
Lutz
TestWritingRootFiles.zip (13.3 KB)

Hello again,

i just found the solution to the first question. If I manualy initialize the values of TArrayC in the constructor of MyPuls by writing

	MyPuls(MyChannel* pc):
		parentChannel(pc),
		idxFiP(0)		{data.fArray=0; data.fN=0;}

the program doesn’t throw an exeption anymore.

The other questions still remain.

Thank you,
Lutz

[quote]2.) Usually a Channel has less Pulses than the container provides. The correct number is stored in MyChannel::nbrPulses. How can I tell the tree not to store the whole container, but only the contents that are necessary?
3.) Instead of using a std::vector is it better to have a TClonesArray container? Can I tell the TClonesArray Container wich contents to write to the tree? [/quote]If I read correctly your code, your pre-allocate objects in the vector<MyPuls*> and then ‘use’ only the necessary objects at the beginning of the vector. ROOT does not have any short cut to let you do that with an std::vector. (technically the vector is being streamed ‘separately’ and would not be able to use the value of ‘nbrPulses’ (not even counting the fact that it would not know the semantic thereof :slight_smile:).
You could write a custom streamer for your class MyClass and look yourself over the usefull element of the vector.

However :slight_smile:, TClonesArray already implemented what you are trying to do :slight_smile:
With a TClonesArray you would simply do:

[code]//______________________________________________________________________________________________________________________
MyPuls* MyChannel::addPuls()
{
//increase number of pulses by one
new (pulses[nbrPulses]) MyPuls(this);

//return the last element
++nbrPulses;
return pulses[nbrPulses -1];

}

void MyChannel::clear()
{
//go through all Pulses and clear them//
pulses->Clear(“C”); // you need to implement MyPuls::Clear
//set the currently used pulses to 0//
nbrPulses = 0;
}[/code]

and it will save only the active pulse object (and avoid most of the memory new/delete)

Cheers,
Philippe

[quote]5.) There are values in MyEvent and MyChannel that are not going to change for each following event. Can those values be stored in some kind of tree header as they will just increase the memory that is needed for each event? [/quote]Yes, you can add your object to the list returned by TTree::GetUserInfo (this will be saved only once per TTree).

Cheers,
Philippe.

[quote]4.) Right now everything is done with ACLIC. Can I also do
the same thing in a Visual Studio 2003 Project? What besides creating a dictionary do I have to do to make it run, once its running with ACLIC? [/quote]Yes you can. You will indeed need to find a way to run rootcint. Then simply compile and link all the source file (including the dictionary) as usual. (See the result root-config --cflags and root-config --libs for the necessary compiler and linker switches).

Cheers,
Philippe.

Hi Philippe,

thanks for all the answers.

[quote]If I read correctly your code, your pre-allocate objects in the vector<MyPuls*> and then ‘use’ only the necessary objects at the beginning of the vector. ROOT does not have any short cut to let you do that with an std::vector. (technically the vector is being streamed ‘separately’ and would not be able to use the value of ‘nbrPulses’ (not even counting the fact that it would not know the semantic thereof Smile).
You could write a custom streamer for your class MyClass and look yourself over the usefull element of the vector.
[/quote]
What I was trying to do with this approach is indeed to avoid unnecessary new - delete operations by allocating lots of MyPulses and then just using the ones I need. A call to MyChannel::addPuls() only uses the new command, if there are not enough Pulses in the “Buffer”.

[quote] //increase number of pulses by one new (pulses[nbrPulses]) MyPuls(this); [/quote]
I am not familiar with this semantics. What exactly does this do? I have seen this in the jets.c tutorial, but I could not figure out how this avoids new - delete operations. Is this semantic only calling a new everytime I make a call to MyChannel::addPuls()? Or does the container somehow remember how many MyPulses have already been allocated and does a new only if there are not enough MyPulses already available? A quick guess would lead me to the conclusion that this call makes a new operation on MyPuls(this) which is typecasted to the return value of pulses[nbrPulses] which is already a Pointer to a MyPuls object? Can you enlighten me on that?

Thank you,
Lutz

[quote]What I was trying to do with this approach is indeed to avoid unnecessary new - delete operations by allocating lots of MyPulses and then just using the ones I need. A call to MyChannel::addPuls() only uses the new command, if there are not enough Pulses in the “Buffer”. [/quote]So TClonesArray is exactly the implementation you are looking for :slight_smile:

This is a new with placement.
The call to TClonesArray::operator[] allocates the memory exactly once for each index.
So on the first run

will allocate a memory area of sizeof(MyPuls) (let’s m0) and return m0 which is used by the new with placement to simply run the MyPuls constructor on it.
On the second run:

new (pulses[0]) MyPuls(this); new (pulses[1]) MyPuls(this);pulses[0] will return the same value ‘m0’ (and hence will not need to allocate any memory.
pulses[1] will allocate a memory area of sizeof(MyPuls).

The TClonesArray::Clear will NOT free any memory. If you pass the option “C” it will call the member function Clear on the objects that were active. TClonesArray::Clear will set the number of active element to zero.

Cheers,
Philippe.

PS. See the User’s guide chapter on collection for more details.

Hi Philippe,

thanks for the detailed information. I slowly start understanding the concept. I modified the classes MyChannel and MyPuls to include the TClonesArray. The class MyPuls now inherits public from TObject. Also I added the ClassDef and ClassImp macro. The MyPuls::Clear() Method reads now void Clear() {data.fArray=0;data.fN=0;}

There are quite a few changes in MyChannel the Constructors and Destructors now look as follows:[code]public:
MyChannel():
parentEvent(0),
nbrPulses(0),
pulses(),
rawdata(0),
rawLength(0),
rawLengthBuffer(0) {}

MyChannel(MyEvent *pe):
  parentEvent(pe),
  nbrPulses(0),
  pulses("MyPuls",10),
  rawdata(0),
  rawLength(0),
  rawLengthBuffer(0)	{}

~MyChannel()	{}

[/code] where pulses is now declared as TClonesArray pulses; //Container storing the pulses
As far as I understand there is no need to delete the entries of the TClonesArray since the dtor of TClonesArray will do that for me.

The functions MyChannel::addPuls() and MyChannel::clear() are as you suggested: [code]MyPuls* MyChannel::addPuls()
{
//add a puls to the container
MyPuls *p = new (pulses[nbrPulses++]) MyPuls(this);

//and return it
return p;

}
void MyChannel::clear()
{
//go through all Pulses and clear them//
pulses.Clear(“C”); // you need to implement MyPuls::Clear
//set the currently used pulses to 0//
nbrPulses = 0;
}[/code]
Everything works fine while writing the tree. But somehow when reading the tree the following error appears:

Error in <TBufferFile::CheckByteCount>: object of class MyChannel read too few bytes: 3127 instead of 3131

I tried to have a Pointer to the TClonesArray and use it like it is done in JetEvent.cxx but this still created the same error. What is it that I am doing wrong? I included the modified testprogram to this post.

I have read the Chapter on Containers in the Users Guide, but it couldn’t help me. Reading other Post with this error suggest that I am using different streamers. But how can that be since its the same program that writes and reads the tree?

Thank you,
Lutz
TestWritingRootFiles.zip (33.6 KB)

Hi,

In your constructor you must initialized all the data members (to avoid random behavior).
The problem is probably in MyPuls where you manipulate directly (and incorrectly I think) the internal of TArrayC. Please use the data member function.

Cheers,
Philippe.

Hi Philippe,

first of all the good news: I found the problem. In the class MyChannel, MyChannel::parentEvent was not recorded or not read. After a bit of playing I found that rearranging the declarations of the variables in MyChannels made the trick. Now MyChannel::parentEvent is declared before the TClonesArray of MyPulses. With this change it did work.

So I got brave and tried to substitut the std::vector<MyChannel*> in MyEvent with a TClonesArray of MyChannels. Ever since I made that change the ROOT-File is recorded but somehow only one event can be read from it. This event doesn’t necessarily need to be the first event. You can also read the second event and then you can’t read the third event. But there is a way to circumvent this behaviour: You delete the event manually and set it to 0 after each iteration. Since this is not the recommended way of reading a Tree, I have two questions:

1.) Is it in general possible to have a TClonesArray of a Class that itself contains another TClonesArray of another Class?

2.) Is there something obviously wrong with my implementation that prevents the recommended way of reading a tree to read more than one event?

Thank you,
Lutz

ps: I have changed the implementation of MyPuls. Now it is not using the TArrayC anymore.
TestWritingRootFiles.zip (13.7 KB)