go-hep.org/x/hep@v0.38.1/groot/rcmd/split.go (about)

     1  // Copyright ©2020 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 rcmd
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"path"
    11  
    12  	"go-hep.org/x/hep/groot"
    13  	"go-hep.org/x/hep/groot/riofs"
    14  	"go-hep.org/x/hep/groot/rtree"
    15  )
    16  
    17  // Split splits the tree from the input file into multiple trees,
    18  // each with nevts entries.
    19  // Split returns the name of the split output files, and an error, if any.
    20  func Split(oname, fname, tname string, nevts int64, verbose bool) ([]string, error) {
    21  	f, err := groot.Open(fname)
    22  	if err != nil {
    23  		return nil, fmt.Errorf(
    24  			"could not open input file %q: %w",
    25  			fname, err,
    26  		)
    27  	}
    28  	defer f.Close()
    29  
    30  	o, err := riofs.Dir(f).Get(tname)
    31  	if err != nil {
    32  		return nil, fmt.Errorf(
    33  			"could not fet tree %q: %w", tname, err,
    34  		)
    35  	}
    36  
    37  	tree, ok := o.(rtree.Tree)
    38  	if !ok {
    39  		return nil, fmt.Errorf("object %q is not a Tree", tname)
    40  	}
    41  
    42  	var (
    43  		cur    int64
    44  		n      = tree.Entries()
    45  		nfiles = 0
    46  	)
    47  	for i := 0; cur < n; i++ {
    48  		m, err := split(oname, tname, tree, i, cur, nevts, verbose)
    49  		if err != nil {
    50  			return nil, fmt.Errorf("could not split tree into file#%d: %w", i, err)
    51  		}
    52  		cur += m
    53  		nfiles++
    54  	}
    55  
    56  	onames := make([]string, nfiles)
    57  	for i := range onames {
    58  		onames[i] = fmt.Sprintf(
    59  			"%s-%d.root",
    60  			oname[:len(oname)-len(".root")], i,
    61  		)
    62  	}
    63  
    64  	return onames, nil
    65  }
    66  
    67  func split(oname, tname string, tree rtree.Tree, i int, beg, nevts int64, verbose bool) (int64, error) {
    68  	oname = fmt.Sprintf(
    69  		"%s-%d.root",
    70  		oname[:len(oname)-len(".root")], i,
    71  	)
    72  	o, err := groot.Create(oname)
    73  	if err != nil {
    74  		return 0, fmt.Errorf("could not create output file %d: %w", i, err)
    75  	}
    76  	defer o.Close()
    77  
    78  	var (
    79  		dirName = path.Dir(tname)
    80  		objName = path.Base(tname)
    81  		dir     = riofs.Directory(o)
    82  	)
    83  	if dirName != "/" && dirName != "" && dirName != "." {
    84  		_, err = riofs.Dir(o).Mkdir(dirName)
    85  		if err != nil {
    86  			return 0, fmt.Errorf("could not create output directory %q: %w", dirName, err)
    87  		}
    88  		odir, err := riofs.Dir(o).Get(dirName)
    89  		if err != nil {
    90  			return 0, fmt.Errorf("could not fetch output directory %q: %w", dirName, err)
    91  		}
    92  		dir = odir.(riofs.Directory)
    93  	}
    94  	wvars := rtree.WriteVarsFromTree(tree)
    95  	w, err := rtree.NewWriter(
    96  		dir, objName,
    97  		wvars,
    98  		rtree.WithTitle(tree.Title()),
    99  	)
   100  	if err != nil {
   101  		return 0, fmt.Errorf("could not create tree writer: %w", err)
   102  	}
   103  	defer w.Close()
   104  
   105  	var (
   106  		rvars = make([]rtree.ReadVar, len(wvars))
   107  		src   = tree
   108  		end   = beg + nevts
   109  	)
   110  	for i, wvar := range wvars {
   111  		rvars[i] = rtree.ReadVar{
   112  			Name:  wvar.Name,
   113  			Value: wvar.Value,
   114  		}
   115  	}
   116  
   117  	if end > tree.Entries() {
   118  		end = tree.Entries()
   119  	}
   120  
   121  	r, err := rtree.NewReader(src, rvars, rtree.WithRange(beg, end))
   122  	if err != nil {
   123  		return 0, fmt.Errorf("could not create tree reader: %w", err)
   124  	}
   125  	defer r.Close()
   126  
   127  	if verbose {
   128  		log.Printf("splitting [%d, %d) into %q...", beg, end, oname)
   129  	}
   130  
   131  	_, err = rtree.Copy(w, r)
   132  	if err != nil {
   133  		return 0, fmt.Errorf("rtree: could not copy tree: %w", err)
   134  	}
   135  
   136  	if verbose {
   137  		log.Printf("splitting [%d, %d) into %q... [ok]", beg, end, oname)
   138  	}
   139  
   140  	return end - beg, nil
   141  }