go-hep.org/x/hep@v0.40.0/groot/rnpy/column.go (about)

     1  // Copyright ©2022 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package rnpy
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  
    11  	"codeberg.org/sbinet/npyio/npy"
    12  	"go-hep.org/x/hep/groot/root"
    13  	"go-hep.org/x/hep/groot/rtree"
    14  )
    15  
    16  var (
    17  	_ npy.Descr       = (*root.Float16)(nil)
    18  	_ npy.Marshaler   = (*root.Float16)(nil)
    19  	_ npy.Unmarshaler = (*root.Float16)(nil)
    20  
    21  	_ npy.Descr       = (*root.Double32)(nil)
    22  	_ npy.Marshaler   = (*root.Double32)(nil)
    23  	_ npy.Unmarshaler = (*root.Double32)(nil)
    24  )
    25  
    26  // NewColumns returns all the ReadVars of the provided Tree as
    27  // a slice of Columns.
    28  //
    29  // ReadVars that can not be represented as NumPy arrays are silently discarded.
    30  func NewColumns(tree rtree.Tree) []Column {
    31  	var (
    32  		rvars = rtree.NewReadVars(tree)
    33  		cols  []Column
    34  	)
    35  
    36  	for _, rvar := range rvars {
    37  		rv := reflect.ValueOf(rvar.Value).Elem()
    38  		switch rv.Kind() {
    39  		case reflect.Chan, reflect.Interface,
    40  			reflect.Struct, reflect.Slice, reflect.Map,
    41  			reflect.Pointer, reflect.UnsafePointer:
    42  			continue
    43  		}
    44  		cols = append(cols, Column{
    45  			tree: tree,
    46  			rvar: rvar,
    47  			etyp: reflect.TypeOf(rvar.Value).Elem(),
    48  		})
    49  	}
    50  
    51  	return cols
    52  }
    53  
    54  // Column provides a NumPy representation of a Branch or Leaf.
    55  type Column struct {
    56  	tree rtree.Tree
    57  	rvar rtree.ReadVar
    58  	etyp reflect.Type
    59  }
    60  
    61  // NewColumn returns the column with the provided name and tree.
    62  //
    63  // NewColumn returns an error if no branch or leaf could be found.
    64  // NewColumn returns an error if the branch or leaf is of an unsupported type.
    65  func NewColumn(tree rtree.Tree, rvar rtree.ReadVar) (Column, error) {
    66  	var (
    67  		rvars = rtree.NewReadVars(tree)
    68  		idx   = -1
    69  		col   Column
    70  	)
    71  
    72  	for i := range rvars {
    73  		if rvars[i].Name == rvar.Name && (rvars[i].Leaf == rvar.Leaf || rvar.Leaf == "") {
    74  			idx = i
    75  			break
    76  		}
    77  	}
    78  
    79  	if idx < 0 {
    80  		name := rvar.Name
    81  		if rvar.Leaf != "" {
    82  			name += "." + rvar.Leaf
    83  		}
    84  		return col, fmt.Errorf("rnpy: no rvar named %q", name)
    85  	}
    86  	rvar = rvars[idx]
    87  
    88  	rv := reflect.ValueOf(rvar.Value).Elem()
    89  	switch rv.Kind() {
    90  	case reflect.Chan, reflect.Interface,
    91  		reflect.Struct, reflect.Slice, reflect.Map,
    92  		reflect.Pointer, reflect.UnsafePointer:
    93  		return col, fmt.Errorf("rnpy: invalid branch or leaf type %T", rv.Interface())
    94  	}
    95  
    96  	col = Column{
    97  		tree: tree,
    98  		rvar: rvar,
    99  		etyp: reflect.TypeOf(rvar.Value).Elem(),
   100  	}
   101  	return col, nil
   102  }
   103  
   104  // Name returns the branch name this Column is bound to.
   105  func (col Column) Name() string {
   106  	return col.rvar.Name
   107  }
   108  
   109  // Slice reads the whole data slice from the underlying ROOT Tree
   110  // into memory.
   111  func (col Column) Slice() (sli any, err error) {
   112  	r, err := rtree.NewReader(col.tree, []rtree.ReadVar{col.rvar})
   113  	if err != nil {
   114  		return nil, fmt.Errorf(
   115  			"rnpy: could not create ROOT reader for %q: %w",
   116  			col.rvar.Name, err,
   117  		)
   118  	}
   119  	defer r.Close()
   120  
   121  	var (
   122  		n     = col.tree.Entries()
   123  		rtyp  = reflect.SliceOf(col.etyp)
   124  		data  = reflect.ValueOf(col.rvar.Value).Elem()
   125  		slice = reflect.MakeSlice(rtyp, int(n), int(n))
   126  		i     int
   127  	)
   128  
   129  	err = r.Read(func(ctx rtree.RCtx) error {
   130  		slice.Index(i).Set(data)
   131  		i++
   132  		return nil
   133  	})
   134  	if err != nil {
   135  		return nil, fmt.Errorf(
   136  			"rnpy: could not read ROOT data for %q: %w",
   137  			col.rvar.Name, err,
   138  		)
   139  	}
   140  
   141  	return slice.Interface(), nil
   142  }