go-hep / hep

hep is the mono repository holding all of go-hep.org/x/hep packages and tools
https://go-hep.org
BSD 3-Clause "New" or "Revised" License
230 stars 35 forks source link

Scanning a tree with custom Root objects #474

Open aphecetche opened 5 years ago

aphecetche commented 5 years ago

I'm trying to read a Root file containing trees of custom root objects using rtree.Scanner. Is that supposed to work ?

I've naively tried :

> root-gen-type -p main -t AliTrackReference -o alitrackreference.go alice_sim_2018_LHC18l8a4_TrackRefs.root
> cat alitrackreference.go
...
type AliTrackReference struct {
        base0       rbase.Object // base class
        fTrack      int32        // Track number
        fX          float32      // X reference position of the track
        fY          float32      // Y reference position of the track
        fZ          float32      // Z reference position of the track
        fPx         float32      // momentum
        fPy         float32      // momentum
        fPz         float32      // momentum
        fLength     float32      // track lenght from its origin in cm
        fTime       float32      // time of flight in cm
        fUserId     int32        // optional Id defined by user
        fDetectorId int32        // Detector Id
}
...
> trackrefs alice_sim_2018_LHC18l8a4_TrackRefs.root
./TrackRefs.root (TFile)
TrackRefs.root/Event0 (TDirectoryFile)
TrackRefs.root/Event0/TreeTR (TTree)
2019/03/06 16:16:51 Tree TreeTR 1204 entries
2019/03/06 16:16:51 rtree: Tree "TreeTR" has no branch named "base0"

where trackrefs program is basically :

func doTree(tree rtree.Tree) {
        log.Printf("Tree %s %d entries\n", tree.Name(), tree.Entries())
        var data AliTrackReference
        sc, err := rtree.NewScanner(tree, &data)
        if err != nil {
                log.Fatal(err)
        }
        defer sc.Close()
        for sc.Next() {
                err := sc.Scan()
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Printf("entry[%d]: %+v\n", sc.Entry(), data)
        }
}

func main() {
        flag.Parse()

        f, err := groot.Open(flag.Arg(0))
        if err != nil {
                log.Fatal(err)
        }
        defer f.Close()

        err = riofs.Walk(f, func(path string, obj root.Object, err error) error {
                fmt.Printf("%s (%s)\n", path, obj.Class())
                if obj.Class() == "TTree" {
                        doTree(obj.(rtree.Tree))
                }
                return nil
        })
        if err != nil {
                log.Fatalf("could not walk through file: %v", err)
        }
}

The input file can be found at : https://cernbox.cern.ch/index.php/s/hshoBqSBIweBM9o

sbinet commented 5 years ago

There's indeed a missing link between the scanner and the type that's being generated from the steamer: right now, IIRC, the scanner isn't clever enough to recognize the generated type implements UnmarshalROOT and it tries to emulate it by reflection. But it's confused by the current handling of 'inheritance' in C++.

The type generator should also probably add struct-tags to the fields to be sure refactoring on the Go side doesn't interfer with the naming of C++ branches...

I'll give it a stab. (But I am on the train to Paris right now)

sbinet commented 5 years ago

A little update: it will probably take a bit more time then anticipated (shocking!) The data written in the file has been written in split mode (which is pretty common, I must admit)

Right now, Groot supports full-split mode or no-split mode. But it has still a hard time with in-the-middle split mode. I'll have a look in my train ride back to Clermont-Fd (no WiFi, checkered 4G coverage: the ideal dev environment)

aphecetche commented 5 years ago

Thanks for looking into this !