Problems with TStreamer of MCParticle attributes ROOT::Math::XYZPoint

Hi everyone,
I am quite enthusiastic about the features provided by your software! I like the RootObjectWriter for output due to the nice compression and file design, but I am not a ROOT person at all, more of a python type. I would prefer using the uproot package to read the data but it constructs itself surprisingly difficult. Maybe I am just a bit stupid, but I just cannot get it done properly… I would be very happy about some help/ enlightenment… so don’t judge me too hard, if my question does not make sense, is obvious or whatsoever :smiley:

Some first struggles I could handle after some contact with the maintainers there, so that I can now read stuff so far that I get the individual entries from the MCParticle vector which have currently the following entries:

first_entry._fields = 
['local_5f_start_5f_point_5f_',
'global_5f_start_5f_point_5f_',
'local_5f_end_5f_point_5f_',
'global_5f_end_5f_point_5f_',
'particle_5f_id_5f_',
'time_5f_',
'parent_5f_',
'track_5f_']

This is quite nice already and I can wrap this into so-called uproot-methods for proper getter methods for our project and everyone else. We want to simulate a time-of-flight system, so particle_id, parent and time are already awesome to get in this easy fashion. To create nice views of the particles hitting the detector though, it would be great to access the hit positions in the global and local start/end points.
At uproot though, we (well, the maintainer to be honest) hit a hard stop though trying to read the contents of the ROOT::Math::XYZPoint entries for the MCParticles, which are the local and global start and end points. There seems to be a problem with the embedded TStreamer describing the data which does not correctly deserialize the data types. The mentioned data types all include 4 byte objects which seem not to make to much sense to the interpreter. By extending uproot with a custom class (see code included in github issue mentioned above), the uproot maintainer could help me read the file. It basically just skips the first 20 byte entries (6 byte headers and 4 byte randomness each for the bunch of ROOT::Math::PositionVector3D<ROOT::Math::Cartesian3D<double>,). This allows me to do something like

particle = t["detector1"].array()[0][0] # first particle in detector 1 in event 1
position = particle._global_5f_start_5f_point_5f_
print(position._x, position._y, position._z) # --> this is the hit i am after basically

Weird about this is also, that the ROOT Streamer indicates a wrong version number 0 which should not be. This might help maybe towards understanding what’s going on.
I suspect, the bit

/**
* @brief ROOT class definition
*/
ClassDefOverride(MCParticle, 6);
/**
* @brief Default constructor for ROOT I/O
*/
MCParticle() = default;

might have something to do with all this.

So the questions are:

  • is this a dumb question?
  • do you have experiences with uproot?
  • do you think there might be something with the TStreamers embedded in MCParticle (and potentially other classes)?
  • can I/ we do something about it? Reading data with uproot is so nice compared to PyROOT

Thanks so much to everyone taking the time reading through this bit

Hi @flome

thanks for reporting! This is indeed very odd I must say - and certainly not a dumb question. Unfortunately I myself have not used uproot (yet) so I’m probably of little help from that side.

What I can tell you from our side is that we are using the standard ROOT utilities for object introspection. The lines you quoted from the source code are exactly this: the ClassDefOverride expands as macro which allows ROOT to build dictionaries for this class. The 6 is a version number, i.e. each time a data member of the class changes, we increment the number so the resulting dictionary reads the class properly and without confusing data type.

The MCParticle() = default; is just the lazy modern C++ way of defining a standard constructor without arguments.

So I fear I’m at loss here - the ROOT::Math::XYZPoint classes seem to be fairly standard and at least we used them everywhere. Maybe you could open a ticket with the ROOT guys? Usually it helps providing a minimum working example, i.e. having a class with a XYZPoint and writing that to file, demonstrating the issue you have. This at least excludes it would be a weird thing introduced by Allpix Squared.

Cheers,
Simon

Hi @simonspa,
as always, thanks for the quick reply! I will look further into this!

In fact, the ROOT TBrowser tells me upon opening the root result files, that no dictionaries are available for those data types. Is that normal or might it result from a similar issue? Sorry for my lack of ROOT knowledge, I am poking around in the dark a bit.
Cheers,
Florian

Hi @flome

the reason for this is that you didn’t tell ROOT where to look for them. :slight_smile: I.e. you need to specifically load the Allpix Squared object library and the respective dictionary. Have a look at our ROOT (or pyROOT) example analysis scripts in the repository:

The README also explains how to load the library at runtime. I hope this helps - storing and reading ROOT objects is always a bit painful, but it’s also quite powerful as it allows us to retain all object properties from the simulation.

Cheers,
Simon