Storing vector<string> class member in TTree

Dear rooters.

@pcanal I’m trying to store some event information in a TTree, where each event could contain information about several particles depositing energy in several detectors. I have a problem figuring out the most convenient format, and specifically I see that vector<string> behaves in a different way than vector<double> when I try to store them as members of a c++ class.

So far, I have come up with the following ‘Hit’-class:

class Hit : public TObject {
  public:
    char const *id;   //Particle identity, i.e. alpha, proton ...
    double e;         //Reconstructed kinetic energy of particle.
    vector<std::string> det;  //List of detectors that were hit.
    std::vector<double> edep; //Energy deposited in the individual detectors.

  ClassDef(Hit,1);
};

and I store the hits in a TClonesArray. In ‘Minimal.C’ (Minimal.C (1.2 KB) ) I construct a TTree (attached in test.root (6.4 KB) ) with one event containing two hits, by running root -l Minimal.c in the terminal. When I load the tree and do a Scan, I get the output:

root [1] data->Scan("id:e:det:edep")
***********************************************************************
*    Row   * Instance *        id *         e *       det *      edep *
***********************************************************************
*        0 *        0 *     alpha *        12 *        D1 *         3 *
*        0 *        1 *    proton *        25 *        D1 *         9 *
*        0 *        2 *           *           *           *         8 *
*        0 *        3 *           *           *           *        13 *
***********************************************************************

‘edep’ shows the wanted behaviour, i.e., there are two values associated with each hit, and I can draw ‘edep’-spectra gated on the particle type, like data->Draw("edep","id==\"alpha\""). The problem is that the Scan only shows ‘D1,D1’, whereas I would expect ‘D1,D2,D2,D3’. Also, I get wrong results when I try to draw the ‘edep’-spectrum gated on a particular detector, i.e. data->Draw("edep","det==\"D3\"") produces an empty spectrum.

How do I get the ‘det’ member to behave in the same way as the ‘edep’ member? I should say that I’m completely open to suggestions of other event formats, as long as I get the possibility to draw spectra gated on particle ID and detector ID.

Cheers,


ROOT Version: 6.14/02
Platform: Ubuntu 16.04
Compiler: gcc 7.4.0


An advantageous option is to store a “detector id” instead of a string. Besides working as you need, it would also save a lot of disk space (and a bit of run-time performance) since the string takes more space on it.
So you could have:

class Hit : public TObject {
  public:
    char const *id;   //Particle identity, i.e. alpha, proton ...
    double e;         //Reconstructed kinetic energy of particle.
    vector<short> det;  //List of detectors that were hit [this could even be a std::vector<char> if there is less than 127 detectors.
    std::vector<double> edep; //Energy deposited in the individual detectors.

  ClassDef(Hit,1);
};

Then you can have a routine

const char *DetIdToName(short);  //[edited function can not return an object]

that does the ID to name conversion and then use

data->Scan("id:e:DetIdToName(det):edep")

Cheers,
Philippe.

Thanks Phillipe, that could be a solution. I ran into a problem when I tested this, though: As suggested I changed the ‘det’ member of ‘Hit’ to a vector<char>, such that

class Hit : public TObject {
  public:
    char const *id;   //Particle identity, i.e. alpha, proton ...
    double e;         //Reconstructed kinetic energy of particle.
    std::vector<char> det;  //List of detectors that were hit.
    std::vector<double> edep; //Energy deposited in the individual detectors.

  ClassDef(Hit,1);
};

and I created a tree similar to the one in my original post, test_2.root (6.4 KB) . I then wrote this ‘ToDetector’ routine

#include <map>
#include <string>

std::string ToDetector(char id)
{
  std::map<char,std::string> table = {{0,"D1"},{1,"D2"},{2,"D3"}};
  return table.find(id)->second;
}

to translate the detector ID into a detector name. When I run a scan, however, I get this error message:

root [3] data->Scan("id:e:ToDetector(det):edep")
Error in <TTreeFormula::Compile>:  TFormula can only call interpreted and compiled functions that return a numerical type: "ToDetector(det)"
***********************************************************************
*    Row   * Instance *        id *         e * ToDetecto *      edep *
***********************************************************************
*        0 *        0 *     alpha *        12 *           *         3 *
*        0 *        1 *    proton *        25 *           *         9 *
*        0 *        2 *           *           *           *         8 *
*        0 *        3 *           *           *           *        13 *
***********************************************************************

Could you point me towards my mistake?

Cheers,
Jonas.

My bad. The function should return the const char*.

Cheers,
Philippe.

Hmm, I tried to have it return const char * instead:

#include <map>

const char * ToDetector(char id)
{
  std::map<char, const char *> table = {{0,"D1"},{1,"D2"},{2,"D3"}};
  return table.find(id)->second;
}

but it looks like ROOT prefers to interpret the output as a number of the order 1.4e14:

root [2] data->Scan("id:e:ToDetector(det):edep")
***********************************************************************
*    Row   * Instance *        id *         e * ToDetecto *      edep *
***********************************************************************
*        0 *        0 *     alpha *        12 * 1.401e+14 *         3 *
*        0 *        1 *    proton *        25 * 1.401e+14 *         9 *
*        0 *        2 *           *           * 1.401e+14 *         8 *
*        0 *        3 *           *           * 1.401e+14 *        13 *
***********************************************************************

Does this behaviour make sense to you? I tried to cast the output from ‘ToDetector()’, but ROOT was not happy with that either:

root [3] data->Scan("id:e:(char*)ToDetector(det):edep")
Warning in <TTreeFormula::DefinedVariable>: Casting to primary types like "char" is not supported yet
Error in <TTreeFormula::Compile>:  Bad numerical expression : "(char*)ToDetector(det)"

Yuck … :frowning: … Indeed I can reproduce the bug … (it does work with data member function though :frowning: ).

Maybe RDataFrame can help here (@eguiraud)?

Uhm I’m not sure RDF can help since TTreeReader (and therefore RDF) can’t read TBranchObjects (and hits is a TBranchObject iiuc).

Sorry!

When you say that it works with a data member function, do you mean that ‘ToDetector()’ should be a member of the ‘Hit’ class?

Yes. I.e. Literally, I tried a class inheriting from TObject and calling GetName() …

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.