diff --git a/groot/gen.rboot.go b/groot/gen.rboot.go index 7f5c4033d..e08a3483c 100644 --- a/groot/gen.rboot.go +++ b/groot/gen.rboot.go @@ -96,7 +96,7 @@ var ( "TLeafF", "TLeafD", "TLeafF16", "TLeafD32", "TLeafC", - "TNtuple", + "TNtuple", "TNtupleD", "TTree", } ) diff --git a/groot/rcmd/dump_test.go b/groot/rcmd/dump_test.go index c70b9c43c..e7cfa28cd 100644 --- a/groot/rcmd/dump_test.go +++ b/groot/rcmd/dump_test.go @@ -52,6 +52,56 @@ func TestDump(t *testing.T) { [000][branch1.floatleaf]: 15.5 [000][branch2.intleaf]: 20 [000][branch2.floatleaf]: 781.2 +`, + }, + { + name: "../testdata/tntuple.root", + want: `key[000]: ntup;1 "my ntuple title" (TNtuple) +[000][x]: 0 +[000][y]: 0.5 +[001][x]: 1 +[001][y]: 1.5 +[002][x]: 2 +[002][y]: 2.5 +[003][x]: 3 +[003][y]: 3.5 +[004][x]: 4 +[004][y]: 4.5 +[005][x]: 5 +[005][y]: 5.5 +[006][x]: 6 +[006][y]: 6.5 +[007][x]: 7 +[007][y]: 7.5 +[008][x]: 8 +[008][y]: 8.5 +[009][x]: 9 +[009][y]: 9.5 +`, + }, + { + name: "../testdata/tntupled.root", + want: `key[000]: ntup;1 "my ntuple title" (TNtupleD) +[000][x]: 0 +[000][y]: 0.5 +[001][x]: 1 +[001][y]: 1.5 +[002][x]: 2 +[002][y]: 2.5 +[003][x]: 3 +[003][y]: 3.5 +[004][x]: 4 +[004][y]: 4.5 +[005][x]: 5 +[005][y]: 5.5 +[006][x]: 6 +[006][y]: 6.5 +[007][x]: 7 +[007][y]: 7.5 +[008][x]: 8 +[008][y]: 8.5 +[009][x]: 9 +[009][y]: 9.5 `, }, { diff --git a/groot/rdict/cxx_root_streamers_gen.go b/groot/rdict/cxx_root_streamers_gen.go index 9e60e349b..3f44a43e4 100644 --- a/groot/rdict/cxx_root_streamers_gen.go +++ b/groot/rdict/cxx_root_streamers_gen.go @@ -5268,6 +5268,34 @@ func init() { Factor: 0.000000, }.New()}, })) + StreamerInfos.Add(NewCxxStreamerInfo("TNtupleD", 1, 0x8de8d873, []rbytes.StreamerElement{ + NewStreamerBase(Element{ + Name: *rbase.NewNamed("TTree", "Tree descriptor (the main ROOT I/O class)"), + Type: rmeta.Base, + Size: 0, + ArrLen: 0, + ArrDim: 0, + MaxIdx: [5]int32{0, 1919213695, 0, 0, 0}, + Offset: 0, + EName: "BASE", + XMin: 0.000000, + XMax: 0.000000, + Factor: 0.000000, + }.New(), 20), + &StreamerBasicType{StreamerElement: Element{ + Name: *rbase.NewNamed("fNvar", "Number of columns"), + Type: rmeta.Int, + Size: 4, + ArrLen: 0, + ArrDim: 0, + MaxIdx: [5]int32{0, 0, 0, 0, 0}, + Offset: 0, + EName: "int", + XMin: 0.000000, + XMax: 0.000000, + Factor: 0.000000, + }.New()}, + })) StreamerInfos.Add(NewCxxStreamerInfo("TTree", 20, 0x7264e07f, []rbytes.StreamerElement{ NewStreamerBase(Element{ Name: *rbase.NewNamed("TNamed", "The basis for a named object (name, title)"), diff --git a/groot/riofs/gendata/gen-tntuple.go b/groot/riofs/gendata/gen-tntuple.go new file mode 100644 index 000000000..f85a6c6e3 --- /dev/null +++ b/groot/riofs/gendata/gen-tntuple.go @@ -0,0 +1,46 @@ +// Copyright ©2022 The go-hep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore +// +build ignore + +package main + +import ( + "flag" + "log" + + "go-hep.org/x/hep/groot/internal/rtests" +) + +var ( + root = flag.String("f", "test-tntuple.root", "output ROOT file") +) + +func main() { + flag.Parse() + + out, err := rtests.RunCxxROOT("gentntuple", []byte(script), *root) + if err != nil { + log.Fatalf("could not run ROOT macro:\noutput:\n%v\nerror: %+v", string(out), err) + } +} + +const script = ` +void gentntuple(const char* fname) { + int bufsize = 32000; + int evtmax = 10; + + auto f = TFile::Open(fname, "RECREATE"); + auto t = new TNtuple("ntup", "my ntuple title", "x:y"); + + for (int i = 0; i != evtmax; i++) { + t->Fill(i, i+0.5); + } + f->Write(); + f->Close(); + + exit(0); +} +` diff --git a/groot/riofs/gendata/gen-tntupled.go b/groot/riofs/gendata/gen-tntupled.go new file mode 100644 index 000000000..dbab7e23d --- /dev/null +++ b/groot/riofs/gendata/gen-tntupled.go @@ -0,0 +1,46 @@ +// Copyright ©2022 The go-hep Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore +// +build ignore + +package main + +import ( + "flag" + "log" + + "go-hep.org/x/hep/groot/internal/rtests" +) + +var ( + root = flag.String("f", "test-tntupled.root", "output ROOT file") +) + +func main() { + flag.Parse() + + out, err := rtests.RunCxxROOT("gentntuple", []byte(script), *root) + if err != nil { + log.Fatalf("could not run ROOT macro:\noutput:\n%v\nerror: %+v", string(out), err) + } +} + +const script = ` +void gentntuple(const char* fname) { + int bufsize = 32000; + int evtmax = 10; + + auto f = TFile::Open(fname, "RECREATE"); + auto t = new TNtupleD("ntup", "my ntuple title", "x:y"); + + for (int i = 0; i != evtmax; i++) { + t->Fill(i, i+0.5); + } + f->Write(); + f->Close(); + + exit(0); +} +` diff --git a/groot/riofs/riofs.go b/groot/riofs/riofs.go index cf90a7cc3..9d94b9a32 100644 --- a/groot/riofs/riofs.go +++ b/groot/riofs/riofs.go @@ -26,6 +26,8 @@ import ( //go:generate go run ./gendata/gen-tlv.go -f ../testdata/tlv-split00.root -split=0 //go:generate go run ./gendata/gen-tlv.go -f ../testdata/tlv-split01.root -split=1 //go:generate go run ./gendata/gen-tlv.go -f ../testdata/tlv-split99.root -split=99 +//go:generate go run ./gendata/gen-tntuple.go -f ../testdata/tntuple.root +//go:generate go run ./gendata/gen-tntupled.go -f ../testdata/tntupled.root // Directory describes a ROOT directory structure in memory. type Directory interface { diff --git a/groot/rtree/reader.go b/groot/rtree/reader.go index b940b718d..7f5625e1f 100644 --- a/groot/rtree/reader.go +++ b/groot/rtree/reader.go @@ -253,6 +253,10 @@ func newReader(t Tree, rvars []ReadVar, n int, beg, end int64) reader { switch t := t.(type) { case *ttree: return newRTree(t, rvars, n, beg, end) + case *tntuple: + return newRTree(&t.ttree, rvars, n, beg, end) + case *tntupleD: + return newRTree(&t.ttree, rvars, n, beg, end) case *chain: return newRChain(t, rvars, n, beg, end) case *join: diff --git a/groot/rtree/tree.go b/groot/rtree/tree.go index 71b12e17e..1783de409 100644 --- a/groot/rtree/tree.go +++ b/groot/rtree/tree.go @@ -646,6 +646,10 @@ type tntuple struct { nvars int } +func (*tntuple) RVersion() int16 { + return rvers.Ntuple +} + func (*tntuple) Class() string { return "TNtuple" } @@ -668,6 +672,37 @@ func (nt *tntuple) UnmarshalROOT(r *rbytes.RBuffer) error { return r.Err() } +type tntupleD struct { + ttree + nvars int +} + +func (*tntupleD) RVersion() int16 { + return rvers.NtupleD +} + +func (*tntupleD) Class() string { + return "TNtupleD" +} + +func (nt *tntupleD) UnmarshalROOT(r *rbytes.RBuffer) error { + if r.Err() != nil { + return r.Err() + } + + beg := r.Pos() + /*vers*/ _, pos, bcnt := r.ReadVersion(nt.Class()) + + if err := nt.ttree.UnmarshalROOT(r); err != nil { + return err + } + + nt.nvars = int(r.ReadI32()) + + r.CheckByteCount(pos, bcnt, beg, nt.Class()) + return r.Err() +} + type tioFeatures uint8 func (*tioFeatures) Class() string { return "TIOFeatures" } @@ -739,6 +774,13 @@ func init() { } rtypes.Factory.Add("TNtuple", f) } + { + f := func() reflect.Value { + o := &tntupleD{} + return reflect.ValueOf(o) + } + rtypes.Factory.Add("TNtupleD", f) + } } var ( @@ -753,6 +795,11 @@ var ( _ Tree = (*tntuple)(nil) _ rbytes.Unmarshaler = (*tntuple)(nil) + _ root.Object = (*tntupleD)(nil) + _ root.Named = (*tntupleD)(nil) + _ Tree = (*tntupleD)(nil) + _ rbytes.Unmarshaler = (*tntupleD)(nil) + _ root.Object = (*tioFeatures)(nil) _ rbytes.Marshaler = (*tioFeatures)(nil) _ rbytes.Unmarshaler = (*tioFeatures)(nil) diff --git a/groot/rvers/versions_gen.go b/groot/rvers/versions_gen.go index 8be41ee4b..e7533295d 100644 --- a/groot/rvers/versions_gen.go +++ b/groot/rvers/versions_gen.go @@ -104,5 +104,6 @@ const ( LeafD32 = 1 // ROOT version for TLeafD32 LeafC = 1 // ROOT version for TLeafC Ntuple = 2 // ROOT version for TNtuple + NtupleD = 1 // ROOT version for TNtupleD Tree = 20 // ROOT version for TTree ) diff --git a/groot/testdata/tntuple.root b/groot/testdata/tntuple.root new file mode 100644 index 000000000..dd9725667 Binary files /dev/null and b/groot/testdata/tntuple.root differ diff --git a/groot/testdata/tntupled.root b/groot/testdata/tntupled.root new file mode 100644 index 000000000..4987dc17d Binary files /dev/null and b/groot/testdata/tntupled.root differ