Error in TRefArray::AddAt:The object at .. is not registered

Hello and Happy New Year!

Since v5.20/00 of ROOT, the following errors appear when reading our ROOT data files:

The ROOT files in question have the following structure:
[ul]1. a TTree containing an unsplit “event” object-branch, the “event” class contains a TClonesArray of "particles"
2. a “multidetector” object containing the list of all the detectors used in the experiment.
3. a TProcessID object[/ul]

Each “particle” has a TRefArray of references to the detectors through which it passed (or more precisely, what is used is a class derived from TRefArray). It is these references which give the errors reported above (basically one error for every detector hit by each particle in each event…= a lot). Apparently, in previous versions the TRefArray’s would automatically adopt the TProcessID in the TFile as their fPID, but since the modifications brought to TRefArray in v5.20 this is no longer the case, they have the fPID of the current session.

An(other) aspect of this problem that I don’t understand is that the errors only come when analysing the data with a TSelector, and not if one simply opens the file, connects the TTree to an “event” object and then reads the data with “TTree::GetEntry(…)”.

I have found a workaround for this problem, but I am worried that I am only treating the symptoms and not the root cause of the problem (no pun intended). What I have done is to override TRefArray::AddAt in my derived class:

[code]void KVRList::AddAt(TObject *obj, Int_t idx)
{
// Add object at position ids. Give an error when idx is out of bounds
// (i.e. the array is not expanded).

if (!obj) return;
// Check if the object can belong here
TProcessID* pid = TProcessID::GetProcessWithUID(obj);
if(fPID != pid) fPID =pid;
TRefArray::AddAt(obj,idx);
}[/code]

i.e. if the TProcessID of the object is not that of the TRefArray, the TRefArray automatically adopts the TProcessID of the object. I did this after checking that in our case all of the objects in question have the TProcessID of the TFile.

All comments/suggestions are welcome… :laughing:
Thanks a lot
John

Hi John,

By default the TRefArray ‘points’ to the current process and can only
receive object that have been created in this process.
To point the TRefArray to a different process you must do: TRefArray array( processId );

For example, if ‘obj’ is an instance that was created in the different
process and you do: TRefArray array( TProcessID::GetProcessWithUID( obj ) );
Then array.Add(obj);
is correct (obj comes from the process the array is pointed to
while TObject *nobj = new TObject; array.Add(nobj);
is incorrect since ‘nobj’ was created in a different process than the
one the array is pointed to. In this case you will see error message: Error in <TRefArray::AddAtAndExpand>: The object at 0x... is not registered in the process the TRefArray point to (pid = ProcessID../....)

The modification you made to AddAt is inherently wrong for example in the following case:obj1 = GetObjectFromProcess_One(); obj2 = GetObjectFromProcess_Two(); refarray->AddAt(obj1); refarray->AddAt(obj2);at then end the first entry of the array in invalid and unusable since refarray points to the 2nd process but the 1st element belongs to the 1st (and I suspect this might even appear to run if the id for obj1 is also in process two!).

Cheers,
Philippe.

Hi Philippe,

Thanks for the reply and the very clear explanations. Could you also give me an answer to the following specific points:

Is this true ?

Can you explain this behaviour ?

Finally, what I would really like is to recover the behaviour of TRefArray in previous versions of ROOT, i.e. when reading TRefArrays in a TFile they (seem to) automatically use the TProcessID from the file as their process ID. What is the correct way to do this with ROOT versions >v5.20 ?

Thanks a lot for your help
John

[quote]Apparently, in previous versions the TRefArray’s would automatically adopt the TProcessID in the TFile as their fPID[/quote]No it wasn’t. It was accepting any object from any process without any warning. (And hence if you un-intentionally filled it with object from a different process you may or may not have been able to retrieve them later.

Cheers,
Philippe.

[quote]the errors only come when analysing the data with a TSelector, and not if one simply opens the file, connects the TTree to an “event” object and then reads the data with “TTree::GetEntry(…)”.

Can you explain this behaviour [/quote]Isn’t the real difference the fact that that TSelector adds object to the TRefArray?

Cheers,
Philippe.

[quote]Finally, what I would really like is to recover the behaviour of TRefArray in previous versions of ROOT, i.e. when reading TRefArrays in a TFile they (seem to) automatically use the TProcessID from the file as their process ID. What is the correct way to do this with ROOT versions >v5.20 ? [/quote]Strange, a priori, the TRefArray should be set to have the same ProcessID as the process that create the TRefArray and hence the file. I must be missing a step or two in the chain of event. [Which actually makes me ask how did you determine that “they (seem to) automatically use the TProcessID from the file” and that they no longer do?

Cheers,
Philippe.

Hi Philippe,

Thanks again for the answers, especially the first one!

Regarding the TSelector question, no it doesn’t add any objects to the TRefArray during the analysis. A priori, it should just be reading the contents of the TRefArray’s, in exactly the same way as if I connect the branch to an object myself and use TTree::GetEntry. In the latter case there are no TRefArray::AddAt errors.

The answer to the question in your third reply is on your first reply! In fact the TRefArray in <v5.20 just didn’t care in which process the objects were created. So I was apparently exploiting without knowing it a bug in TRefArray which has now been fixed.

I’m going to try to write some kind of short, simple example to show what I want to do and why it doesn’t work…

Well, after a hard day’s work on the question I think I no longer understand anything at all!!! I have attached what started out as an attempt to give the simplest possible example of my problem. In the end, I am not able to reproduce the problem I first reported; instead, I now have another problem which I will try to explain.

Un-tar the .tar.gz, ‘cd’ to the directory TRefArrayExample directory you just created, and type ‘make’ to build the shared library libTRAE.so (if ROOTSYS is not defined, you’ll need to type ‘make ROOT_MAKEFILE_PATH=/some/path…’ with the path to wherever $ROOTSYS/test/Makefile.arch is hidden on your system).

Once the .so has been built, you can create a ROOT file containing a few detectors and a TTree with 10 events (each event contains a TClonesArray of particles, each particle contains a TRefArray with references to the detectors). Launch ROOT and do:

root [0] .L libTRAE.so
root [1] .L TRAEMain.cxx+
root [2] TRAEMain m
root [3] m.WriteTree()

You should see something like:

Info in <TRAEDetector::Streamer>: Called
Info in <TRAEDetector::Streamer>: Called
Info in <TRAEDetector::Streamer>: Called
Info in <TRAEDetector::Streamer>: Called
Info in <TRAEDetector::Streamer>: Called
 ### EVENT NUMBER 1 ###
Info in <TRAEEvent::Print>: Event with 10 particles :
TRAEParticle::Part_1_Event_1 fNumDet=1
OBJ: TRAEDetector	Det_1	TRAEDetector object
TRAEParticle::Part_2_Event_1 fNumDet=2
OBJ: TRAEDetector	Det_1	TRAEDetector object
OBJ: TRAEDetector	Det_2	TRAEDetector object
TRAEParticle::Part_3_Event_1 fNumDet=5
OBJ: TRAEDetector	Det_1	TRAEDetector object
OBJ: TRAEDetector	Det_2	TRAEDetector object
OBJ: TRAEDetector	Det_3	TRAEDetector object
OBJ: TRAEDetector	Det_4	TRAEDetector object
OBJ: TRAEDetector	Det_5	TRAEDetector object
TRAEParticle::Part_4_Event_1 fNumDet=2
OBJ: TRAEDetector	Det_1	TRAEDetector object
OBJ: TRAEDetector	Det_2	TRAEDetector object

where you can see for each particle of each event, the number of associated detectors (fNumDet), and the list of detectors.
Now quit ROOT and start a new session, this time do

root [0] .L libTRAE.so   
root [1] TFile f("TRefArrayExampleTree.root")
root [2] .ls
TFile**		TRefArrayExampleTree.root	
 TFile*		TRefArrayExampleTree.root	
  KEY: TRAEDetector	Det_1;1	TRAEDetector object
  KEY: TRAEDetector	Det_2;1	TRAEDetector object
  KEY: TRAEDetector	Det_3;1	TRAEDetector object
  KEY: TRAEDetector	Det_4;1	TRAEDetector object
  KEY: TRAEDetector	Det_5;1	TRAEDetector object
  KEY: TProcessID	ProcessID0;1	926cfe4e-e18e-31dd-8001-9a3cca1cdd73
  KEY: TTree	Run;1	TTree with event objects

The referenced detectors are written in the file, along with the TProcessID object. Now read in the detector objects:

root [3] f.Get("Det_1")
Info in <TRAEDetector::Streamer>: Called
(class TObject*)0x917f1d0
root [4] f.Get("Det_2")
Info in <TRAEDetector::Streamer>: Called
(class TObject*)0x90a0208

etc. etc. and then connect the TTree and read an event:

root [5] TRAEEvent* event = 0
root [6] Run->SetBranchAddress("Event",&event)
root [7] Run->GetEntry(0)
Info in <TRAEEvent::Streamer>: Called
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=1
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=2
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=5
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=2
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=3
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=5
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=4
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=3
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=4
Collection name='TRefArray', class='TRefArray', size=5
Info in <TRAEParticle::Streamer>: Called
Read fNumDet=4
Collection name='TRefArray', class='TRefArray', size=5
(Int_t)(913)
root [8] event->Print()
Info in <TRAEEvent::Print>: Event with 10 particles :
TRAEParticle::Part_1_Event_1 fNumDet=1
TRAEParticle::Part_2_Event_1 fNumDet=2
TRAEParticle::Part_3_Event_1 fNumDet=5
TRAEParticle::Part_4_Event_1 fNumDet=2
TRAEParticle::Part_5_Event_1 fNumDet=3
TRAEParticle::Part_6_Event_1 fNumDet=5
TRAEParticle::Part_7_Event_1 fNumDet=4
TRAEParticle::Part_8_Event_1 fNumDet=3
TRAEParticle::Part_9_Event_1 fNumDet=4
TRAEParticle::Part_10_Event_1 fNumDet=4

As you can see, the number of detectors was correctly stored, but the references to the detectors have been lost. On further investigation, the TRefArray of each particle is not in fact empty, it does contain the correct ‘unique ID’ for each detector it references, the fPID of the TRefArray is the right one (i.e. the one in the TFile), but the object cannot be retrieved from the TProcessID. In TRefArray::At, the call to fPID->GetObjectWithID returns 0x0, event though both fPID and the ID are correct:

inline TObject *TRefArray::At(Int_t at) const
{
   // Return the object at position i. Returns 0 if i is out of bounds.
   int j = at-fLowerBound;
   if (j >= 0 && j < fSize) {
      if (!fPID) return 0;
      /*** obj = 0x0 after the next line ***/
      TObject *obj = fPID->GetObjectWithID(fUIDs[j]);
     /*** even though fPID and fUIDs[j] are correct ***/
      if (obj==0) obj = GetFromTable(j);
      return obj;
   }
   BoundsOk("At", at);
   return 0;
}

I suppose I must now be doing something completely wrong. Can you help me please ?
Thanks a lot.
John
TRefArrayExample.tar.gz (62 KB)

Hi John,

The issue is that the Detector object are stored without their unique id. The unique id is assigned to a TObject only at the time they are first referenced. The simpliest solution is to store the detector after processing the tree:[code] // Loop creating events and filling tree
for(Int_t i=1; i<=n_events; i++){
FillEvent(i);
cout << " ### EVENT NUMBER " << i << " ###" << endl;
Event->Print();
RootTree->Fill();
Event->Clear();
}

// Write detectors in ROOT file
DetectorList->Write();

// Write TTree in file
RootTree->Write();[/code]

Alternatively you can simply assigned using detector to a dummy TRef (and re-use the same TRef for all detectors) before writing them to the file.

Cheers,
Philippe.

:blush: Thanks a lot, that helps ENORMOUSLY!! :blush:

So now everything works fine, even with a TSelector if I read in the detectors from the file in the Notify() method the events are read fine and there are no errors from TRefArray::AddAt...

I also tried writing the TTree with ROOT v5.19/03 and reading it with v5.22/00 - still no problem…