groot

package
v0.36.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2024 License: BSD-3-Clause Imports: 3 Imported by: 28

README

groot

GoDoc

Experimental, pure-Go package to read and write ROOT files, without having ROOT installed.

Installation

go get go-hep.org/x/hep/groot

Documentation

groot documentation can be found over there:

https://godoc.org/go-hep.org/x/hep/groot

Documentation

Overview

Package groot provides a pure-go read/write-access to ROOT files.

A typical usage is as follows:

f, err := groot.Open("ntup.root")
if err != nil {
    log.Fatal(err)
}
defer f.Close()

obj, err := f.Get("tree")
if err != nil {
    log.Fatal(err)
}
tree := obj.(rtree.Tree)
fmt.Printf("entries= %v\n", tree.Entries())

More complete examples on how to iterate over the content of a Tree can be found in the examples attached to groot.TreeScanner and groot.Scanner: https://godoc.org/go-hep.org/x/hep/groot/rtree#pkg-examples

Another possibility is to look at: https://godoc.org/go-hep.org/x/hep/groot/cmd/root-ls, a command that inspects the content of ROOT files.

File layout

ROOT files are a suite of consecutive data records. Each data record consists of a header part, called a TKey, and a payload whose content, length and meaning are described by the header. The current ROOT file format encodes all data in big endian.

ROOT files initially only supported 32b addressing. Large files support (>4Gb) was added later on by migrating to a 64b addressing.

The on-disk binary layout of a ROOT file header looks like this:

     Type        | Record Name | Description
=================+=============+===========================================
   [4]byte       | "root"      | Root file identifier
   int32         | fVersion    | File format version
   int32         | fBEGIN      | Pointer to first data record
   int32 [int64] | fEND        | Pointer to first free word at the EOF
   int32 [int64] | fSeekFree   | Pointer to FREE data record
   int32         | fNbytesFree | Number of bytes in FREE data record
   int32         | nfree       | Number of free data records
   int32         | fNbytesName | Number of bytes in TNamed at creation time
   byte          | fUnits      | Number of bytes for file pointers
   int32         | fCompress   | Compression level and algorithm
   int32 [int64] | fSeekInfo   | Pointer to TStreamerInfo record
   int32         | fNbytesInfo | Number of bytes in TStreamerInfo record
   [18]byte      | fUUID       | Universal Unique ID
=================+=============+===========================================

This is followed by a sequence of data records, starting at the fBEGIN offset from the beginning of the file.

The on-disk binary layout of a data record is:

      Type     | Member Name | Description
===============+=============+===========================================
 int32         | Nbytes      | Length of compressed object (in bytes)
 int16         | Version     | TKey version identifier
 int32         | ObjLen      | Length of uncompressed object
 int32         | Datime      | Date and time when object was written to file
 int16         | KeyLen      | Length of the key structure (in bytes)
 int16         | Cycle       | Cycle of key
 int32 [int64] | SeekKey     | Pointer to record itself (consistency check)
 int32 [int64] | SeekPdir    | Pointer to directory header
 byte          | lname       | Number of bytes in the class name
 []byte        | ClassName   | Object Class Name
 byte          | lname       | Number of bytes in the object name
 []byte        | Name        | Name of the object
 byte          | lTitle      | Number of bytes in the object title
 []byte        | Title       | Title of the object
 []byte        | DATA        | Data bytes associated to the object
===============+=============+===========================================

The high-level on-disk representation of a ROOT file is thus:

+===============+ -- 0
|               |
|  File Header  |
|               |
+===============+ -- fBEGIN offset
|               |
| Record Header | -->-+
|               |     |
+---------------+     |
|               |     |
|  Record Data  |     | Reference to next Record
|    Payload    |     |
|               |     |
+===============+ <---+
|               |
| Record Header | -->-+
|               |     |
+---------------+     |
|               |     |
|  Record Data  |     | Reference to next Record
|    Payload    |     |
|               |     |
+===============+ <---+
|               |
       ...

|               |
+===============+ -- fSeekInfo
|               |
| Record Header | -->-+
|               |     |
+---------------+     |
|               |     |
|  Record Data  |     | Reference to next Record
|    Payload    |     |
|               |     |
+===============+ <---+ -- fEND offset

Data records payloads and how to deserialize them are described by a TStreamerInfo. The list of all TStreamerInfos that are used to interpret the content of a ROOT file is stored at the end of that ROOT file, at offset fSeekInfo.

Data records

Data records' payloads may be compressed. Detecting whether a payload is compressed is usually done by comparing the object length (ObjLen) field of the record header with the length of the compressed object (Nbytes) field. If they differ after having subtracted the record header length, then the payload has been compressed.

A record data payload is itself split into multiple chunks of maximum size 16*1024*1024 bytes. Each chunk consists of:

  • the chunk header,
  • the chunk compressed payload.

The chunk header:

  • 3 bytes to identify the compression algorithm and version,
  • 3 bytes to identify the deflated buffer size,
  • 3 bytes to identify the inflated buffer size.

Streamer informations

Streamers describe how a given type, for a given version of that type, is written on disk. In C++/ROOT, a streamer is represented as a TStreamerInfo class that can give metadata about the type it's describing (version, name). When reading a file, all the streamer infos are read back in memory, from disk, by reading the data record at offset fSeekInfo. A streamer info is actually a list of streamer elements, one for each field and, in C++, base class (in Go, this is emulated as an embedded field.)

go generate groot

groot is a pure-Go package, but to ensure two-way compatibility with ROOT/C++ (ie: groot can read files created with ROOT/C++ and ROOT/C++ can read files created with groot), the groot package may require a proper ROOT/C++ installation. This is the case, e.g., with the 'go generate go-hep.org/x/hep/groot' command. This command will invoke ROOT/C++ to create ROOT files and to inspect a ROOT/C++ installation for some metadata (ROOT version, TStreamerXXX versions, etc...)

Installation of ROOT/C++ is documented here:

https://root.cern/install/

groot should remain buildable without a ROOT/C++ installation.

Index

Examples

Constants

View Source
const (
	Version = root.Version // ROOT version hep/groot implements
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Array

type Array interface {
	Len() int // number of array elements
	Get(i int) interface{}
	Set(i int, v interface{})
}

Array describes ROOT abstract array type.

type Collection

type Collection interface {
	Object

	// Name returns the name of the collection.
	Name() string

	// Last returns the last element index
	Last() int

	// At returns the element at index i
	At(i int) Object

	// Len returns the number of elements in the collection
	Len() int
}

Collection is a collection of ROOT Objects.

type File

type File = riofs.File

func Create

func Create(name string, opts ...FileOption) (*File, error)

Create creates the named ROOT file for writing.

Example
package main

import (
	"fmt"
	"log"
	"os"

	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/rbase"
	"go-hep.org/x/hep/groot/root"

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	const fname = "objstring.root"
	defer os.Remove(fname)

	w, err := groot.Create(fname)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Close()

	var (
		k = "my-objstring"
		v = rbase.NewObjString("Hello World from Go-HEP!")
	)

	err = w.Put(k, v)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("wkeys: %d\n", len(w.Keys()))

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

	r, err := groot.Open(fname)
	if err != nil {
		log.Fatalf("could not open file: %v", err)
	}
	defer r.Close()

	fmt.Printf("rkeys: %d\n", len(r.Keys()))

	for _, k := range r.Keys() {
		fmt.Printf("key: name=%q, type=%q\n", k.Name(), k.ClassName())
	}

	obj, err := r.Get(k)
	if err != nil {
		log.Fatal(err)
	}
	rv := obj.(root.ObjString)
	fmt.Printf("objstring=%q\n", rv)

}
Output:

wkeys: 1
rkeys: 1
key: name="my-objstring", type="TObjString"
objstring="Hello World from Go-HEP!"
Example (EmptyFile)
package main

import (
	"fmt"
	"log"
	"os"

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

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	const fname = "empty.root"
	defer os.Remove(fname)

	w, err := groot.Create(fname)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Close()

	// empty file. close it.
	err = w.Close()
	if err != nil {
		log.Fatalf("could not close empty file: %v", err)
	}

	// read back.
	r, err := groot.Open(fname)
	if err != nil {
		log.Fatalf("could not open empty file: %v", err)
	}
	defer r.Close()

	fmt.Printf("file: %q\n", r.Name())

}
Output:

file: "empty.root"
Example (WithZlib)
package main

import (
	"compress/flate"
	"fmt"
	"log"
	"os"

	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/rbase"
	"go-hep.org/x/hep/groot/riofs"
	"go-hep.org/x/hep/groot/root"

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	const fname = "objstring-zlib.root"
	defer os.Remove(fname)

	w, err := groot.Create(fname, riofs.WithZlib(flate.BestCompression))
	if err != nil {
		log.Fatal(err)
	}
	defer w.Close()

	var (
		k = "my-objstring"
		v = rbase.NewObjString("Hello World from Go-HEP!")
	)

	err = w.Put(k, v)
	if err != nil {
		log.Fatal(err)
	}

	err = w.Close()
	if err != nil {
		log.Fatalf("could not close writable file: %v", err)
	}

	r, err := groot.Open(fname)
	if err != nil {
		log.Fatalf("could not open file: %v", err)
	}
	defer r.Close()

	for _, k := range r.Keys() {
		fmt.Printf("key: name=%q, type=%q\n", k.Name(), k.ClassName())
	}

	obj, err := r.Get(k)
	if err != nil {
		log.Fatalf("could not get key %q: %v", k, err)
	}
	rv := obj.(root.ObjString)
	fmt.Printf("objstring=%q\n", rv)

}
Output:

key: name="my-objstring", type="TObjString"
objstring="Hello World from Go-HEP!"

func NewReader

func NewReader(r Reader) (*File, error)

NewReader creates a new ROOT file reader.

func Open

func Open(path string) (*File, error)

Open opens the named ROOT file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has mode os.O_RDONLY.

Example
package main

import (
	"fmt"
	"log"

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

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	f, err := groot.Open("testdata/simple.root")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	for _, key := range f.Keys() {
		fmt.Printf("key:  %q cycle=%d title=%q\n", key.Name(), key.Cycle(), key.Title())
	}

	obj, err := f.Get("tree")
	if err != nil {
		log.Fatal(err)
	}

	tree := obj.(rtree.Tree)
	fmt.Printf("tree: %q, entries=%d\n", tree.Name(), tree.Entries())

}
Output:

key:  "tree" cycle=1 title="fake data"
tree: "tree", entries=4
Example (File)

ExampleOpen_file shows how users can open a local ROOT file with the 'file://' protocol.

package main

import (
	"fmt"
	"log"

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

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	f, err := groot.Open("file://./testdata/simple.root")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	for _, key := range f.Keys() {
		fmt.Printf("key:  %q cycle=%d title=%q\n", key.Name(), key.Cycle(), key.Title())
	}

	obj, err := f.Get("tree")
	if err != nil {
		log.Fatal(err)
	}

	tree := obj.(rtree.Tree)
	fmt.Printf("tree: %q, entries=%d\n", tree.Name(), tree.Entries())

}
Output:

key:  "tree" cycle=1 title="fake data"
tree: "tree", entries=4
Example (Graph)
package main

import (
	"fmt"
	"log"

	"go-hep.org/x/hep/groot"
	"go-hep.org/x/hep/groot/rhist"

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	f, err := groot.Open("testdata/graphs.root")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	obj, err := f.Get("tg")
	if err != nil {
		log.Fatal(err)
	}

	g := obj.(rhist.Graph)
	fmt.Printf("name:  %q\n", g.Name())
	fmt.Printf("title: %q\n", g.Title())
	fmt.Printf("#pts:  %d\n", g.Len())
	for i := 0; i < g.Len(); i++ {
		x, y := g.XY(i)
		fmt.Printf("(x,y)[%d] = (%+e, %+e)\n", i, x, y)
	}

}
Output:

name:  "tg"
title: "graph without errors"
#pts:  4
(x,y)[0] = (+1.000000e+00, +2.000000e+00)
(x,y)[1] = (+2.000000e+00, +4.000000e+00)
(x,y)[2] = (+3.000000e+00, +6.000000e+00)
(x,y)[3] = (+4.000000e+00, +8.000000e+00)
Example (Mmap)

ExampleOpen_mmap shows how users can open and mmap a local ROOT file. mmap-ing a file may be useful for performances reasons.

package main

import (
	"fmt"
	"log"

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

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	f, err := groot.Open("file+mmap://./testdata/simple.root")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	for _, key := range f.Keys() {
		fmt.Printf("key:  %q cycle=%d title=%q\n", key.Name(), key.Cycle(), key.Title())
	}

	obj, err := f.Get("tree")
	if err != nil {
		log.Fatal(err)
	}

	tree := obj.(rtree.Tree)
	fmt.Printf("tree: %q, entries=%d\n", tree.Name(), tree.Entries())

}
Output:

key:  "tree" cycle=1 title="fake data"
tree: "tree", entries=4
Example (OverXRootD)
package main

import (
	"fmt"
	"log"

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

	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
)

func main() {
	f, err := groot.Open("root://eospublic.cern.ch//eos/root-eos/cms_opendata_2012_nanoaod/Run2012B_DoubleMuParked.root")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	for _, key := range f.Keys() {
		fmt.Printf("key:  %q cycle=%d title=%q\n", key.Name(), key.Cycle(), key.Title())
	}

	obj, err := f.Get("Events")
	if err != nil {
		log.Fatal(err)
	}

	tree := obj.(rtree.Tree)
	fmt.Printf("tree: %q, entries=%d\n", tree.Name(), tree.Entries())

}
Output:

key:  "Events" cycle=1 title="Events"
tree: "Events", entries=29308627

type FileOption

type FileOption = riofs.FileOption

type List

type List interface {
	SeqCollection
}

List is a list of ROOT Objects.

type Named

type Named interface {
	Object

	// Name returns the name of this ROOT object
	Name() string

	// Title returns the title of this ROOT object
	Title() string
}

Named represents a ROOT TNamed object

type ObjArray

type ObjArray interface {
	SeqCollection
	LowerBound() int
}

ObjArray is an array of ROOT Objects.

type ObjString

type ObjString interface {
	Name() string
	String() string
}

ObjString is a ROOT string that implements ROOT TObject.

type Object

type Object interface {
	// Class returns the ROOT class of this object
	Class() string
}

Object represents a ROOT object

type Reader

type Reader = riofs.Reader

type SeqCollection

type SeqCollection interface {
	Collection
}

SeqCollection is a sequential collection of ROOT Objects.

Directories

Path Synopsis
cmd
root-cp
root-cp selects and copies keys from a ROOT file to another ROOT file.
root-cp selects and copies keys from a ROOT file to another ROOT file.
root-diff
root-diff compares the content of 2 ROOT files, including the content of their Trees (for all entries), if any.
root-diff compares the content of 2 ROOT files, including the content of their Trees (for all entries), if any.
root-dump
root-dump dumps the content of a ROOT file, including the content of the Trees (for all entries), if any.
root-dump dumps the content of a ROOT file, including the content of the Trees (for all entries), if any.
root-gen-datareader
Command root-gen-datareader generates a Go struct to easily read the event data type stored inside a Tree.
Command root-gen-datareader generates a Go struct to easily read the event data type stored inside a Tree.
root-gen-rfunc
Command root-gen-rfunc generates a rfunc.Formula based on a function signature or an already existing function.
Command root-gen-rfunc generates a rfunc.Formula based on a function signature or an already existing function.
root-gen-streamer
Command root-gen-streamer generates a StreamerInfo for ROOT and user types.
Command root-gen-streamer generates a StreamerInfo for ROOT and user types.
root-gen-type
Command root-gen-type generates a Go type from the StreamerInfo contained in a ROOT file.
Command root-gen-type generates a Go type from the StreamerInfo contained in a ROOT file.
root-ls
root-ls lists the content of a ROOT file.
root-ls lists the content of a ROOT file.
root-merge
root-merge merges ROOT files' content into a merged ROOT file.
root-merge merges ROOT files' content into a merged ROOT file.
root-print
root-print prints ROOT files contents to PDF, PNG, ...
root-print prints ROOT files contents to PDF, PNG, ...
root-split
root-split splits an input file+tree into multiple output ROOT files, each containing N entries.
root-split splits an input file+tree into multiple output ROOT files, each containing N entries.
root-srv
root-srv runs a web server that can inspect and browse ROOT files.
root-srv runs a web server that can inspect and browse ROOT files.
exp
rntup
Package rntup contains types to handle RNTuple-related data.
Package rntup contains types to handle RNTuple-related data.
internal
httpio
Package httpio provides types for basic I/O interfaces over HTTP.
Package httpio provides types for basic I/O interfaces over HTTP.
rcompress
rcompress provides types and functions to compress and decompress ROOT data payloads.
rcompress provides types and functions to compress and decompress ROOT data payloads.
Package rarrow handles conversion between ROOT and ARROW data models.
Package rarrow handles conversion between ROOT and ARROW data models.
Package rbase contains the definitions of ROOT base classes.
Package rbase contains the definitions of ROOT base classes.
Package rbytes contains the definitions of types useful for serializing and deserializing ROOT data buffers.
Package rbytes contains the definitions of types useful for serializing and deserializing ROOT data buffers.
Package rcmd provides helper functions containing the logic of various root-xyz commands.
Package rcmd provides helper functions containing the logic of various root-xyz commands.
Package rcont contains the definitions of ROOT container types, such as TList, THashList and TObjArray.
Package rcont contains the definitions of ROOT container types, such as TList, THashList and TObjArray.
Package rdict contains the definition of ROOT streamers and facilities to generate new streamers meta data from user types.
Package rdict contains the definition of ROOT streamers and facilities to generate new streamers meta data from user types.
Package rhist contains the interfaces and definitions of ROOT types related to histograms and graphs.
Package rhist contains the interfaces and definitions of ROOT types related to histograms and graphs.
Package riofs contains the types and low-level functions to deal with opening and creating ROOT files, and decoding the internal structure of ROOT files.
Package riofs contains the types and low-level functions to deal with opening and creating ROOT files, and decoding the internal structure of ROOT files.
plugin/http
Package http is a plugin for riofs.Open to support opening ROOT files over http(s).
Package http is a plugin for riofs.Open to support opening ROOT files over http(s).
plugin/xrootd
Package xrootd is a plugin for riofs.Open to support opening ROOT files over xrootd.
Package xrootd is a plugin for riofs.Open to support opening ROOT files over xrootd.
Package rjson contains tools to marshal ROOT objects to JSON.
Package rjson contains tools to marshal ROOT objects to JSON.
Package rmeta provides tools to interoperate with ROOT Meta.
Package rmeta provides tools to interoperate with ROOT Meta.
Package rnpy handles conversions between ROOT and NumPy arrays.
Package rnpy handles conversions between ROOT and NumPy arrays.
Package root defines ROOT core interfaces.
Package root defines ROOT core interfaces.
Package rpad contains the definitions of ROOT graphics base classes.
Package rpad contains the definitions of ROOT graphics base classes.
Package rphys contains definitions for physics-related ROOT classes.
Package rphys contains definitions for physics-related ROOT classes.
Package rsql provides a convenient access to ROOT files/trees as a database.
Package rsql provides a convenient access to ROOT files/trees as a database.
rsqldrv
Package rsqldrv registers a database/sql/driver.Driver implementation for ROOT files.
Package rsqldrv registers a database/sql/driver.Driver implementation for ROOT files.
Package rsrv exposes HTTP end-points to manipulate ROOT files.
Package rsrv exposes HTTP end-points to manipulate ROOT files.
Package rtree contains the interfaces and types to decode, read, concatenate and iterate over ROOT Trees.
Package rtree contains the interfaces and types to decode, read, concatenate and iterate over ROOT Trees.
rfunc
Package rfunc provides types and funcs to implement user-provided formulae evaluated on data exposed by ROOT trees.
Package rfunc provides types and funcs to implement user-provided formulae evaluated on data exposed by ROOT trees.
Package rtypes contains the means to register types (ROOT ones and user defined ones) with the ROOT type factory.
Package rtypes contains the means to register types (ROOT ones and user defined ones) with the ROOT type factory.
Package rvers contains the ROOT version and the classes' versions groot is supporting and currently reading.
Package rvers contains the ROOT version and the classes' versions groot is supporting and currently reading.
Package ztypes holds all the types registered with the rtypes factory.
Package ztypes holds all the types registered with the rtypes factory.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL