RE: [ROOT] Looping over array entries with TTree::Draw

From: Philippe Canal (pcanal@fnal.gov)
Date: Thu Aug 12 2004 - 00:51:34 MEST


Hi Sue,

Thanks you for reporting these problems.  I solved most of them
and this will be uploaded soon in the CVS repository.  The
problem I was able to fix were related to the fact that you have
a (conceptual) array with 2 varying dimensions that the 2nd dimensions
is sometimes 0 (aka fNShower==0) and a fixed index is used.

However there is an issue for which I can not find an elegant 
solution.  The issue is at the level of the 'syntax' of the formula.

When you write 

tree->Scan("fShowers[fEvents[].fShwInd[0]].fEnergy","fEvents[].fNShower > 0");

The result to be expected is (albeit annoyingly in __this__ case, see further
down for a justification on why it is the way it is).

***********************************
*    Row   * Instance * fShowers[ *
***********************************
*        0 *        0 *        10 *
***********************************

The reason for that is that the 'iterators' for an array used as an index
for another array (fEvents[].fShwInd[0] in your example) are independent
of the outer iterators.  In essence the above formula is equivalent to
   for i=0; i< size of fEvents; ++i)
       if (fEvents[i].fNshower>0) {
           return fShowers( func(i) );
       }
   }
where func(n) is defined as
  return nth element in the list of (fEvents[j].fShwInd[0] for all j)
and hence it does
  if (fEvents[0].fNShower>0) then print fShowers[fEvents[0].fShwInd[0]]
  if (fEvents[1].fNShower>0) then print fShowers[fEvents[2].fShwInd[0]]
(this is because there is only 3 fShwInd; fEvents[0].fShwInd[0], 
fEvents[0].fShwInd[1] and fEvents[1].fShwInd[0])

Now it turns out that simply using 
   tree->Scan("fShowers[fEvents[].fShwInd[0]].fEnergy","");
get how what you need
   ***********************************
   *    Row   * Instance * fShowers[ *
   ***********************************
   *        0 *        0 *        10 *
   *        0 *        1 *        30 *
   ***********************************
Also
  tree->Scan("fEvents.fEventNo:fShowers[fEvents[].fShwInd[]].fEnergy","");
was corrected to print (the array are intentionally not synchronized, 
see TTree::Scan documentation)
  ***********************************************
  *    Row   * Instance * fEvents.f * fShowers[ *
  ***********************************************
  *        0 *        0 *         0 *        10 *
  *        0 *        1 *         1 *        20 *
  *        0 *        2 *         2 *        30 *
  *        0 *        3 *         3 *           *
  ***********************************************

To properly synchronize the array use (the condition is used to properly
synchronize the iterators:
 tree->Scan("fEvents.fEventNo:fShowers[fEvents[].fShwInd[]].fEnergy",
             "fEvents[].fShwInd[]*0==0");
  ***********************************************
  *    Row   * Instance * fEvents.f * fShowers[ *
  ***********************************************
  *        0 *        0 *         0 *        10 *
  *        0 *        1 *         0 *        20 *
  *        0 *        2 *         2 *        30 *
  ***********************************************

Also I introduced a new interface where you can select longer columns:
tree->Scan("fEvents.fEventNo:fShowers[fEvents[].fShwInd[]].fEnergy",
           "fEvents[].fShwInd[]*0==0",
           "col=16:37");
**********************************************************************************
*    Row   * Instance * fEvents.fEventNo * fShowers[fEvents[].fShwInd[]].fEnergy *
**********************************************************************************
*        0 *        0 *                0 *                                    10 *
*        0 *        1 *                0 *                                    20 *
*        0 *        2 *                2 *                                    30 *
**********************************************************************************

The syntax in the Scan options is as follow:

   //    colsize=ss
   //       Where 'ss' will be used as the default size for all the column
   //       If this options is not specified, the default column size is 9
   //    precision=pp
   //       Where 'pp' will be used as the default 'precision' for the
   //       printing format.
   //    col=xxx
   //       Where 'xxx' is colon (:) delimited list of printing format for
   //       each column if no format is specified for a column, the default is
   //       used.
   // For example:
   //   tree->Scan("a:b:c","","colsize=30 precision=3 col=::20.10");
   // Will print 3 columns, the first 2 columns will be 30 characters long,
   // the third columns will be 20 characters long.  The printing format used
   // for the columns (assuming they are numbers) will be respectively:
   //   %30.3g %30.3g %20.10g

Now I should explain why I can't really solve the iterator synchronization
problem.  Let's assume a formula like:

   a[i1][i2][i3] + b[i4][c[j1][j2]][i5]

If a,b and c are independent, it is clear that this should unwind to

   for (i1) 
     for (i2)
       for (i3) 
          i4 = i1;
          i5 = i3;
          j1 = func(i2 and size of c)
          j2 = func(i2 and size of c)

However if a and c are related:

   a[i1][i2].d[i3] + b[i4][a[j1][j2].e][i5]

it would seems clear :( that this should unwnd to

   for (i1) 
     for (i2)
       for (i3) 
          i4 = i1;
          j1 = i1;
          j2 = i2;
          i5 = i3; // I am not even sure.

I have not found any straight-forward (read than wont
take me less than a week to implement) ways to detect
properly and safely those cases.   An alternative
that was propose was to allow the use of the 'iterator'
for the user to express those connection.  This is
not simple to implement either.

All in all my choice has been to spend the time I
would need to implement this feature in straightening
and improving the new proxy based replacement for
MakeSelector (aka Running a script for Draw and MakeProxy).

Cheers,
Philippe







-----Original Message-----
From: owner-roottalk@pcroot.cern.ch
[mailto:owner-roottalk@pcroot.cern.ch]On Behalf Of Sue Kasahara
Sent: Friday, August 06, 2004 3:54 PM
To: roottalk
Subject: [ROOT] Looping over array entries with TTree::Draw


Hi roottalk,
I'm having trouble looping over entries in array variables using
TTree::Draw under a specific circumstance.
I've placed an example in:
http://www.hep.umn.edu/~schubert/roottest/ntptest.tar.gz
The example consists of three simple classes:

class NtpShower : public TObject {
...
  Float_t fEnergy; // shower energy

  ClassDef(NtpShower,1) //A reconstructed shower
};

class NtpEvent: public TObject {
...
  Int_t fEventNo; // event number
  Int_t fNShower; // number of showers in this event
  Int_t* fShwInd; //[fNShower] array of indices into shower TClonesArray

  ClassDef(NtpEvent,1) //A reconstructed event
};

class NtpRecord:public TObject {
...
  TClonesArray* fShowers; //~> Array of showers
  TClonesArray* fEvents;    //-> Array of events

  ClassDef(NtpRecord,1) //A reconstructed spill record
};

These classes are set up so that the user can use the shower indices stored
in the NtpEvent class to map back to the NtpShower objects stored in the 
fShowers
TClonesArray.

In the test program FillNtp, a tree is created with a single main branch 
to hold NtpRecord objects,
split at level = 99,  One NtpRecord is generated and the fShowers array 
is filled with 3 NtpShowers:
// shower 0 has fEnergy 10
// shower 1 has fEnergy 20
// shower 2 has fEnergy 30
and 4 NtpEvents:
// event 0 has 2 showers with fShwInd values of 0 &1
// event 1 has 0 showers
// event 2 has 1 shower with fShwInd value of 2
// event 3 has 0 showers

Now I try to use the arrays in a Draw (or Scan loop) and I find that the 
results aren't what I expect.
For example,
  tree -> 
Scan("fShowers[fEvents[].fShwInd[0]].fEnergy","fEvents[].fNShower > 0");
yields:
***********************************
*    Row   * Instance * fShowers[ *
***********************************
*        0 *        0 *        10 *
***********************************
==> 1 selected entry
instead of the expected 2 entries.  I can get 2 entries if I leave off 
the selection string, but I find
I need this selection string to avoid array out of bounds if the first 
argument to Scan or Draw
becomes more complicated, e.g.:
  tree -> Scan("fEvents[].fNShower:fShowers[fEvents[].fShwInd[0]].fEnergy");
causes a rather enormous list of array indexing errors.
Is this a bug or is there an error in the way I'm expressing the loop 
over the arrays?
Thanks,
-Sue
Fixed
root [2] tree->Scan("fEvents.fEventNo:fShowers[fEvents[].fShwInd[]].fEnergy","")                      
***********************************************
*    Row   * Instance * fEvents.f * fShowers[ *
***********************************************
*        0 *        0 *         0 *        10 *
*        0 *        1 *         1 *        20 *
*        0 *        2 *         2 *        30 *

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 1024 (LWP 3560)]
0x4023dc03 in TStreamerInfo::GetValueAux(int, void*, int, int) (type=43, ladd=0x8ce1e34, k=3, len=0)
    at meta/src/TStreamerInfo.cxx:1440
1440	      case kOffsetP + kInt_t:     READ_ARRAY(Int_t)

root [1] tree->Scan("fEvents.fEventNo:fShowers[fEvents[].fShwInd[]].fEnergy","fEvents[].fNShower > 0")
***********************************************
*    Row   * Instance * fEvents.f * fShowers[ *
***********************************************
*        0 *        0 *         0 *        10 *
*        0 *        2 *         2 *        30 *
***********************************************
==> 2 selected entries


root [5] tree->Scan("fEvents.fEventNo*100:fEvents[].fShwInd[]:fShowers[fEvents[].fShwInd[]].fEnergy","fEvents[].fNShower > 0") 
***********************************************************
*    Row   * Instance * fEvents.f * fEvents[] * fShowers[ *
***********************************************************
*        0 *        0 *         0 *         0 *        10 *
*        0 *        1 *         0 *         1 *        20 *
*        0 *        2 *       200 *         2 *        30 *
***********************************************************
==> 3 selected entries
(long long)3

stills wrong:





root [4] tree->Scan("fEvents.fEventNo*100:fEvents[].fShwInd[]:fShowers[fEvents[].fShwInd[0]].fEnergy","fEvents[].fNShower > 0")
***********************************************************
*    Row   * Instance * fEvents.f * fEvents[] * fShowers[ *
***********************************************************
*        0 *        0 *         0 *         0 *        10 *
*        0 *        1 *         0 *         1 *        10 *
*        0 *        2 *       200 *         2 *        30 *
***********************************************************



OK: root [3] tree->Scan("fShowers[fEvents[].fShwInd[0]].fEnergy","fEvents[].fNShower > 0")
***********************************
*    Row   * Instance * fShowers[ *
***********************************
*        0 *        0 *        10 *
*        0 *        2 *        30 *
***********************************
==> 2 selected entries


OK :  root [3] tree->Scan("fEvents.fEventNo*100:fEvents[].fShwInd[]:fShowers","fEvents[].fNShower > 0"); 
***********************************************************
*    Row   * Instance * fEvents.f * fEvents[] *  fShowers *
***********************************************************
*        0 *        0 *         0 *         0 *         0 *
*        0 *        1 *         0 *         1 *         0 *
*        0 *        2 *       200 *         2 *         0 *
***********************************************************
==> 3 selected entries



This archive was generated by hypermail 2b29 : Sun Jan 02 2005 - 05:50:09 MET