Home | News | Documentation | Download

Converting phasespace to .root file

Hi Guys,

I’m trying to convert a phasespace file (binary, 4M histories, x/y/z/px/py etc) to a root file which much have the following leaf-list structure

PhaseSpaceTree
     PhaseSpaceBranch
            x
            y
            ....

Between reading and writing the phasespace must be ordered on eventID (important).
While this seems easy enough, I’ve been struggling for the greater part of three days so any and all help would be appreciated. I’ve been working in python, but if someone could provide me with C code/help I could manage.

So far I’ve managed two things.

  1. Convert the binary file (200MB) to a .root file within a few seconds. However, it’s only PhaseSpaceTree, and then the variables (x/y/z) as branches. No basket called PhaseSpaceBranch. Using numpy2root, so no branches (Maybe this is where my issue is).

  2. Convert the binary file (200MB) to a correct setup with tree → branch → leaves. Done by using pyROOT to create the tree, then the branch, then for every row in a pandas sorted dataframe unpack the row, commit to root data structure, and fill the tree. Takes upwards of 10 mins.

So while option 2 works, it’s not a viable option (720 phasespaces). As it’s been bothering me for awhile, any help would be appreciated. I’ve pretty much exhausted google now.

Working, but snippet of slow code


gROOT.ProcessLine(
"struct data_t {\
   Float_t           x;\
   ...
  Int_t           PB_id;\
};" );


de = pandas.DataFrame(data)
data = ROOT.data_t()
f = TFile( 'testslowin.root', 'RECREATE' )
tree = TTree( 'PhaseSpaceTree', 'Branch recreated' )
tree.Branch( 'PhaseSpaceBranch', data, 'x/F:y/F:z/F:px/F:py/F:pz/F:Ekin/F:wepl/F:pdgID/I:trackID/I:event/I:PB_id/I' )

for row in de.iterrows():
	series = row[1]
	#print(series["x"])
	px = series["py"]
	py = series["py"]
	pz = 1-py*py-px*px
	data.x = series["x"]            # assign as integer
	data.y = series["y"]
	data.z = series["z"]
	data.px = px
	data.py = py
	data.pz = pz
	data.Ekin = series["ekin"]
	data.datawepl = series["w"]
	data.pdgID = series["pdg"]
	data.trackID = series["trackID"]
	data.event = series["eventID"]
	data.PB_id = series["parentID"]

	tree.Fill()
tree.Print()
tree.Write()

ROOT Version: 6.24/02
Platform: Linux
Compiler: Not Provided


It seems this question covers several aspects: Trees, python. Let me ping the relevant root experts (@pcanal @etejedor)

if you could point me to the binary phasespace file, I could give it a try.

I’ve attached a phasespace with the same structure, and it’s accompanying header. 54 bytes per historie, around 4k histories. 200KB binary file, header is text.
Using struct in python with struct.unpack(’=fffffffibbfiiii’,byte) to get 54 bytes width.

Currently trying to use the cpp examples to get working with that, but as you can imagine it’s not going too well (No experience with cpp)

Renamed PhaseSpace.phsp and PhaseSpace.header to PhaseSpace.txt and PhaseSpaceHeader.txt respectively to get around the file upload filter.

Thanks for the help

Unfortunately I can’t upload files as ‘New users can’t add links’, and the file address counts as a link. Suggestions?

Posting this comment upgraded me, so hopefully I can upload now.

PhaseSpace.txt (217.4 KB)
PhaseSpaceHeader.txt (1.1 KB)

here is what I got (using groot):

package main

import (
	"bufio"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"math"
	"os"

	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/rtree"
)

type PhaseSpace struct {
	X float32 `groot:"x"`
	Y float32 `groot:"y"`
	Z float32 `groot:"z"`

	Px float32 `groot:"px"`
	Py float32 `groot:"py"`
	Pz float32 `groot:"pz"`
	E  float32 `groot:"Ekin"`

	Weight float32 `groot:"wepl"`
	Type   int32   `groot:"pdgID"`

	EvtID    int32 `groot:"event"`
	TrkID    int32 `groot:"trackID"`
	ParentID int32 `groot:"PB_ID"`
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: root-phsp [options] raw.phsp out.root

ex:
  $> root-phsp raw.phsp out.root

options:
`)
		flag.PrintDefaults()
	}

	flag.Parse()

	f, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatalf("could not open input PhaseSpace binary file: %+v", err)
	}
	defer f.Close()

	o, err := groot.Create(flag.Arg(1))
	if err != nil {
		log.Fatalf("could not create output ROOT file: %+v", err)
	}
	defer o.Close()

	var (
		buf  = make([]byte, 54)
		data struct {
			PhaseSpace PhaseSpace `groot:"PhaseSpaceBranch"`
		}
		wvars = rtree.WriteVarsFromStruct(&data)
	)

	tree, err := rtree.NewWriter(o, "PhaseSpaceTree", wvars)
	if err != nil {
		log.Fatalf("could not create tree: %+v", err)
	}
	defer tree.Close()

	var (
		bo    = binary.LittleEndian
		r     = bufio.NewReaderSize(f, len(buf))
		nevts int
	)
loop:
	for {
		_, err = r.Read(buf)
		if err != nil {
			if errors.Is(err, io.EOF) {
				break loop
			}
			log.Fatalf("could not read input buffer: %+v", err)
		}
		data.PhaseSpace.X = math.Float32frombits(bo.Uint32(buf[0:4]))
		data.PhaseSpace.Y = math.Float32frombits(bo.Uint32(buf[4:8]))
		data.PhaseSpace.Z = math.Float32frombits(bo.Uint32(buf[8:12]))
		data.PhaseSpace.Px = math.Float32frombits(bo.Uint32(buf[12:16]))
		data.PhaseSpace.Py = math.Float32frombits(bo.Uint32(buf[16:20]))
		data.PhaseSpace.Pz = 1 - data.PhaseSpace.Py*data.PhaseSpace.Py - data.PhaseSpace.Px*data.PhaseSpace.Px
		data.PhaseSpace.E = math.Float32frombits(bo.Uint32(buf[20:24]))
		data.PhaseSpace.Weight = math.Float32frombits(bo.Uint32(buf[24:28]))
		data.PhaseSpace.Type = int32(bo.Uint32(buf[28:32]))
		data.PhaseSpace.EvtID = int32(bo.Uint32(buf[42:46]))
		data.PhaseSpace.TrkID = int32(bo.Uint32(buf[46:50]))
		data.PhaseSpace.ParentID = int32(bo.Uint32(buf[50:54]))

		_, err = tree.Write()
		if err != nil {
			log.Fatalf("could not write tree event: %+v", err)
		}
		nevts++
	}

	log.Printf("written %d events", nevts)

	err = tree.Close()
	if err != nil {
		log.Fatalf("could not close tree writer: %+v", err)
	}

	err = o.Close()
	if err != nil {
		log.Fatalf("could not close output ROOT file: %+v", err)
	}
}

Usage example:

$> ./root-phsp -h
Usage: root-phsp [options] raw.phsp out.root

ex:
  $> root-phsp raw.phsp out.root

options:

$> ./root-phsp ./PhaseSpace.phsp out.root
2021/11/22 15:03:10 written 4122 events

$> root-ls -t ./out.root
=== [./out.root] ===
version: 62400
  TTree              PhaseSpaceTree             (entries=4122)
    PhaseSpaceBranch "PhaseSpaceBranch" TBranchElement

$> root
root [4] struct PhaseSpace {
root (cont'ed, cancel with .@) [5]float x,y,z;
root (cont'ed, cancel with .@) [6]float px,py,pz,Ekin;
root (cont'ed, cancel with .@) [7]float wepl;
root (cont'ed, cancel with .@) [8]int pdgID,event,trackID,PB_ID;
root (cont'ed, cancel with .@) [9]};
root [10] auto f = TFile::Open("./out.root");
root [11] auto t = (TTree*)f->Get("PhaseSpaceTree");
root [12] t->Print("*");
******************************************************************************
*Tree    :PhaseSpaceTree:                                                        *
*Entries :     4122 : Total =             932 bytes  File  Size =        415 *
*        :          : Tree compression factor =   1.00                       *
******************************************************************************
*Br    0 :PhaseSpaceBranch : PhaseSpace                                      *
*Entries :     4122 : Total  Size=     215528 bytes  File Size  =     119075 *
*Baskets :        7 : Basket Size=      32768 bytes  Compression=   1.81     *
*............................................................................*

root [14] t->Scan("PhaseSpaceBranch.x:y:z")
************************************************
*    Row   * PhaseSpac *         y *         z *
************************************************
*        0 * -1.950000 * -0.692135 * 0.0717188 *
*        1 * -1.950000 * -0.400521 * 0.1744139 *
*        2 * -1.950000 * 0.1607809 * -0.619361 *
*        3 * -1.950000 * -0.178247 * -0.640931 *
*        4 * -1.950000 * -0.401852 * -0.190134 *
*        5 * -1.950000 * 0.1284643 * -0.006115 *
*        6 * -1.950000 * -0.645025 * -0.459760 *


Binaries and testdata are available here:

hth,
-s

Hello,
I was wondering if, to speed this up, you could do 1) to have a first fast conversion to ROOT format, then use RDataFrame to read that file and produce the final file with the PhaseSpaceBranch (this would be faster than a loop in Python since the event loop happens in C++ in RDataFrame). Are you familiar at all with RDataFrame? You would need to use operations such as Define and Snapshot.

Hi Sbinet,

Thank you so much, it’s great you whipped up a program so fast. I was very close to figuring it out myself, but yours is probably more stable. Only point would be that the array is not sorted, which is the essential step. However, reading in the binary phasespace file in python, sorting it and writing it back into a sorted binary phasespace is only a ~2/3 second operation. So what I did was sort all 720 files, then used a batch file to perform your program on it (hopefully giving me the correct result, haven’t checked yet.)

Thanks a lot, consider this topic done :slight_smile:

Thanks

Yeah I tried looking into RDataFrame but while Snapshotting went fine, I had issues getting Define running. ROOT and C are a bit too high level code to just quickly get into I noticed. Current setup works, but your suggestion might be faster. This is suffice for now though, but thanks!

Only point would be that the array is not sorted

sorry, I forgot that part.
so, IIUC, one would collect all events from a given phasespace file, sort them (increasing order) on the eventID and write them to the tree.

would this be a correctly describe task? or would you also need to sort eventID across all phasespace files?

assuming the above is true, I’ve created these new binaries:

FYI, here is the code (dealing with only one input phasespace file):

package main

import (
	"bufio"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"math"
	"os"
	"sort"

	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/rtree"
)

type PhaseSpace struct {
	X float32 `groot:"x"`
	Y float32 `groot:"y"`
	Z float32 `groot:"z"`

	Px float32 `groot:"px"`
	Py float32 `groot:"py"`
	Pz float32 `groot:"pz"`
	E  float32 `groot:"Ekin"`

	Weight float32 `groot:"wepl"`
	Type   int32   `groot:"pdgID"`

	EvtID    int32 `groot:"event"`
	TrkID    int32 `groot:"trackID"`
	ParentID int32 `groot:"PB_ID"`
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: root-phsp [options] raw.phsp out.root

ex:
  $> root-phsp raw.phsp out.root

options:
`)
		flag.PrintDefaults()
	}

	flag.Parse()

	f, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatalf("could not open input PhaseSpace binary file: %+v", err)
	}
	defer f.Close()

	evts, err := read(f)
	if err != nil {
		log.Fatalf("could not read PhaseSpace input data: %+v", err)
	}

	o, err := groot.Create(flag.Arg(1))
	if err != nil {
		log.Fatalf("could not create output ROOT file: %+v", err)
	}
	defer o.Close()

	var (
		data struct {
			PhaseSpace PhaseSpace `groot:"PhaseSpaceBranch"`
		}
		wvars = rtree.WriteVarsFromStruct(&data)
	)

	tree, err := rtree.NewWriter(o, "PhaseSpaceTree", wvars)
	if err != nil {
		log.Fatalf("could not create tree: %+v", err)
	}
	defer tree.Close()

	for i := range evts {
		data.PhaseSpace = evts[i]
		_, err = tree.Write()
		if err != nil {
			log.Fatalf("could not write tree event: %+v", err)
		}
	}

	log.Printf("written %d events", len(evts))

	err = tree.Close()
	if err != nil {
		log.Fatalf("could not close tree writer: %+v", err)
	}

	err = o.Close()
	if err != nil {
		log.Fatalf("could not close output ROOT file: %+v", err)
	}
}

func read(src io.Reader) ([]PhaseSpace, error) {
	var (
		buf  = make([]byte, 54)
		bo   = binary.LittleEndian
		r    = bufio.NewReader(src)
		evts []PhaseSpace
	)

loop:
	for {
		_, err := io.ReadFull(r, buf)
		if err != nil {
			if errors.Is(err, io.EOF) {
				break loop
			}
			return nil, fmt.Errorf("could not read input buffer: %w", err)
		}
		var evt PhaseSpace
		evt.X = math.Float32frombits(bo.Uint32(buf[0:4]))
		evt.Y = math.Float32frombits(bo.Uint32(buf[4:8]))
		evt.Z = math.Float32frombits(bo.Uint32(buf[8:12]))
		evt.Px = math.Float32frombits(bo.Uint32(buf[12:16]))
		evt.Py = math.Float32frombits(bo.Uint32(buf[16:20]))
		evt.Pz = 1 - evt.Py*evt.Py - evt.Px*evt.Px
		evt.E = math.Float32frombits(bo.Uint32(buf[20:24]))
		evt.Weight = math.Float32frombits(bo.Uint32(buf[24:28]))
		evt.Type = int32(bo.Uint32(buf[28:32]))
		evt.EvtID = int32(bo.Uint32(buf[42:46]))
		evt.TrkID = int32(bo.Uint32(buf[46:50]))
		evt.ParentID = int32(bo.Uint32(buf[50:54]))

		evts = append(evts, evt)
	}

	sort.Slice(evts, func(i, j int) bool {
		return evts[i].EvtID < evts[j].EvtID
	})

	return evts, nil
}

leading to:

root [5] t->Scan("event:x")
************************************
*    Row   *     event *         x *
************************************
*        0 *         0 * -1.950000 *
*        1 *         2 * -1.950000 *
*        2 *         3 * -1.950000 *
*        3 *         7 * -1.950000 *
*        4 *         8 * -1.950000 *
*        5 *        10 * -1.950000 *
*        6 *        11 * -1.950000 *
*        7 *        16 * -1.950000 *
[...]

I was coincidentally typing when I saw your response. Unfortunately I ran into an issue when trying to parse the date, giving the error Warning in <TClass::Init>: no dictionary for class PhaseSpace is available

Looking into the .root and comparing to a dataset with the intended setup I see two difference (Rightclick each element in the object browser).
Both trees, called PhaseSpaceTree are of type TTree::PhaseSpaceTree.

The branches, in the original dataset, is of type TBranch::PhaseSpaceBranch, while the one output by your binary is of type TBranchElement::PhaseSpaceBranch.
Additionally, the variables x/y/z are of type TLeafF or TLeafI depending on float/integer in the original dataset, while they’re TNonSplitbrowsable on your binary output.

I can’t quite be sure whether it’s opening the branch that fails, or opening the variables. I think its the variables, so the error would be in tree->GetEntry(index)

Help would be much appreciated, if I can help with files/anything/teams meeting let me know :slight_smile:

with the following script:

import ROOT

ROOT.gInterpreter.Declare("""struct PhaseSpace {
    float x,y,z;
    float px,py,pz,Ekin;

    float wepl;
    int pdgID;

    int event, trackID, PB_ID;
};""")

f = ROOT.TFile.Open("./out-sorted.root")

t = f.Get("PhaseSpaceTree")
evt = ROOT.PhaseSpace()
t.SetBranchAddress("PhaseSpaceBranch", ROOT.AddressOf(evt))
for i in range(10):
    t.GetEntry(i)
    print("%d: (x=%s, y=%s, z=%s, px=%s, py=%s, pz=%s, ekin=%s, wepl=%s)" % (i,evt.x, evt.y, evt.z, evt.px, evt.py, evt.pz, evt.Ekin, evt.wepl))
    pass

I got:

$> python ./tst.py 
0: (x=-1.9500000476837158, y=0.18836617469787598, z=-0.23779296875, px=0.9996671676635742, py=-0.019618336111307144, pz=0.00028067827224731445, ekin=86.00328826904297, wepl=1.0)
1: (x=-1.9500000476837158, y=-0.4405491352081299, z=0.034747231751680374, px=0.9999973773956299, py=0.001212602248415351, pz=3.7550926208496094e-06, ekin=86.35478973388672, wepl=1.0)
2: (x=-1.9500000476837158, y=0.22206957638263702, z=0.13809320330619812, px=0.9997751116752625, py=-0.013710787519812584, pz=0.00026172399520874023, ekin=85.99327087402344, wepl=1.0)
3: (x=-1.9500000476837158, y=-0.45038914680480957, z=0.050995271652936935, px=0.9996868968009949, py=0.010956979356706142, pz=0.0005060434341430664, ekin=86.1054916381836, wepl=1.0)
4: (x=-1.9500000476837158, y=0.6851763129234314, z=0.12554951012134552, px=0.9997403621673584, py=0.008404857479035854, pz=0.00044858455657958984, ekin=85.9610366821289, wepl=1.0)
5: (x=-1.9500000476837158, y=0.5481306314468384, z=0.4146670699119568, px=0.9997521638870239, py=0.01045979093760252, pz=0.00038617849349975586, ekin=85.41960906982422, wepl=1.0)
6: (x=-1.9500000476837158, y=0.09405486285686493, z=-0.3721190392971039, px=0.9857691526412964, py=0.16468262672424316, pz=0.0011388063430786133, ekin=86.09940338134766, wepl=1.0)
7: (x=-1.9500000476837158, y=-0.600007176399231, z=-0.5285683870315552, px=0.9999034404754639, py=0.011190210469067097, pz=6.788969039916992e-05, ekin=86.05879211425781, wepl=1.0)
8: (x=-1.9500000476837158, y=-0.5111569166183472, z=0.3312559425830841, px=0.9995656609535217, py=-0.02709799073636532, pz=0.0001341700553894043, ekin=86.38948059082031, wepl=1.0)
9: (x=-1.9500000476837158, y=0.4151419699192047, z=0.5300723314285278, px=0.9999309182167053, py=-0.011713702231645584, pz=9.5367431640625e-07, ekin=85.90902709960938, wepl=1.0)

True, but while python can read the data quite easily, the reconstruction software I’m using is C, which when calling that root file with tree->GetEntry (or tree->GetListOfBranches()) is unfamiliar with the layout of the .root file as the branch has been defined as a PhaseSpace Class.

edit to add full error:

 *** Break *** segmentation violation
 Generating stack trace...
 0x00007f49d6e9f97b in TBranchElement::ReadLeavesMember(TBuffer&) + 0x18b from /home/randy/ROOT/root/lib/libTree.so.6.24
 0x00007f49d6e97740 in TBranch::GetEntry(long long, int) + 0xf0 from /home/randy/ROOT/root/lib/libTree.so.6.24
 0x00007f49d6ea9950 in TBranchElement::GetEntry(long long, int) + 0x170 from /home/randy/ROOT/root/lib/libTree.so.6.24
 0x00007f49d6f10a47 in TTree::GetEntry(long long, int) + 0xc7 from /home/randy/ROOT/root/lib/libTree.so.6.24
root -b -q ./tst.C
   ------------------------------------------------------------------
  | Welcome to ROOT 6.24/06                        https://root.cern |
  | (c) 1995-2021, The ROOT Team; conception: R. Brun, F. Rademakers |
  | Built for linuxx8664gcc on Sep 02 2021, 14:20:23                 |
  | From tags/v6-24-06@v6-24-06                                      |
  | With c++ (GCC) 11.1.0                                            |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q'       |
   ------------------------------------------------------------------


Processing ./tst.C...
i: 0 -- x=-1.950000 ekin=86.003288
i: 1 -- x=-1.950000 ekin=86.354790
i: 2 -- x=-1.950000 ekin=85.993271
i: 3 -- x=-1.950000 ekin=86.105492
i: 4 -- x=-1.950000 ekin=85.961037
i: 5 -- x=-1.950000 ekin=85.419609
i: 6 -- x=-1.950000 ekin=86.099403
i: 7 -- x=-1.950000 ekin=86.058792
i: 8 -- x=-1.950000 ekin=86.389481
i: 9 -- x=-1.950000 ekin=85.909027

with:

#include <TTree.h>
#include <TFile.h>

struct PhaseSpace {
    float x,y,z;
    float px,py,pz,Ekin;

    float wepl;
    int pdgID;

    int event, trackID, PB_ID;
};

void tst() {
	auto f = TFile::Open("./out-sorted.root");
	auto t = (TTree*)f->Get("PhaseSpaceTree");

	auto evt = new PhaseSpace;
	t->SetBranchAddress("PhaseSpaceBranch", &evt);

	for (int i = 0; i < 10; i++) {
		t->GetEntry(i);
		printf("i: %d -- x=%f ekin=%f\n", i, evt->x, evt->Ekin);
	}
}

or:

%> g++ -o read `root-config --libs --cflags` ./read.cxx && ./read 
i: 0 -- x=-1.950000 ekin=86.003288
i: 1 -- x=-1.950000 ekin=86.354790
i: 2 -- x=-1.950000 ekin=85.993271
i: 3 -- x=-1.950000 ekin=86.105492
i: 4 -- x=-1.950000 ekin=85.961037
i: 5 -- x=-1.950000 ekin=85.419609
i: 6 -- x=-1.950000 ekin=86.099403
i: 7 -- x=-1.950000 ekin=86.058792
i: 8 -- x=-1.950000 ekin=86.389481
i: 9 -- x=-1.950000 ekin=85.909027

with read.cxx:

#include <TTree.h>
#include <TFile.h>
#include <TInterpreter.h>

struct PhaseSpace {
    float x,y,z;
    float px,py,pz,Ekin;

    float wepl;
    int pdgID;

    int event, trackID, PB_ID;
};

int main(int argc, char **argv) {
	// declare the backing type *before* opening the file.
	gInterpreter->Declare(R"(
struct PhaseSpace {
    float x,y,z;
    float px,py,pz,Ekin;

    float wepl;
    int pdgID;

    int event, trackID, PB_ID;
};)");
	auto f = TFile::Open("./out-sorted.root");
	auto t = (TTree*)f->Get("PhaseSpaceTree");

	auto evt = new PhaseSpace;
	t->SetBranchAddress("PhaseSpaceBranch", &evt);

	for (int i = 0; i < 10; i++) {
		t->GetEntry(i);
		printf("i: %d -- x=%f ekin=%f\n", i, evt->x, evt->Ekin);
	}

	return 0;
}

I thought that would’ve 100% fixed it, but unfortunately I’m not quite there yet.
Two things are at play. While the sample code works on the root files, the software doesn’t.

The code where the data is being processed casts the .root file to a structure defined as

struct PhaseSpace
{
itk::Vector<float,3> position;
itk::Vector<float,3> direction;
float ekin;
float wepl;
int pdgID;
int trackID;
int eventID;
int pbID;
}

I’ve ran the code with three different input .root files.

  1. The original data, works flawlessly (TTree → TBranch ->TLeafF)
  2. Generated tree, filled with a cxx script (called basic2, code attached) (TTree → TBranch ->TLeafF), or with a very slow python script (same result).
  3. A tree generated output by your binary (TTree → TBranchElement → TNonSplitBrowsable)

Looking at the segmentation faults, getEntry is failed to apply the struct as defined above to a rootfile of category three. I hope I explained it clearly.

Getting close to the brink of insanity though!

basic2.root (1.6 KB)
PhaseSpaceStructur.root (6.1 KB)

do you have the “original data” in a ROOT file?

it seems the PhaseSpace struct your C++ framework expects uses a fixed-size C-array for the position and direction fields.

here is what root-phsp-v3 produces:

$> root-ls -sinfos ./out-sorted.root 
=== [./out-sorted.root] ===
version: 62400
streamer-infos:
 StreamerInfo for "PhaseSpace" version=1 title="PhaseSpace"
  float   position  offset=  0 type= 25 size= 12  
  float   direction offset=  0 type= 25 size= 12  
  float   Ekin      offset=  0 type=  5 size=  4  
  float   wepl      offset=  0 type=  5 size=  4  
  int32_t pdgID     offset=  0 type=  3 size=  4  
  int32_t event     offset=  0 type=  3 size=  4  
  int32_t trackID   offset=  0 type=  3 size=  4  
  int32_t PB_ID     offset=  0 type=  3 size=  4  
[...]

I do, however it’s >100 MB, so I can’t upload it. For visualization a screenshot of the structure. In (visual) structure it’s identical to the PhaseSpaceStrctur.root I added in the previous post. So in the .root it’s still saved as x/y/z floats, while the processing software casts the GetEntry to a structure.

Looking at the segmentation fault I think the problem is with opening the TNonSplitBrowsable. Or it just can’t cast three NonSplitBrowsables to the required structure. Any way to output TTree → TBranch → TLeafs using go?

image

could you paste the output of root-ls -sinfos waterphantom.root ?
“my” root-ls can be obtained from: