go-hep.org/x/hep@v0.38.1/groot/rsql/scan.go (about) 1 // Copyright ©2019 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 rsql provides a convenient access to ROOT files/trees as a database. 6 package rsql // import "go-hep.org/x/hep/groot/rsql" 7 8 import ( 9 "fmt" 10 "io" 11 "math" 12 "reflect" 13 14 "go-hep.org/x/hep/groot/rsql/rsqldrv" 15 "go-hep.org/x/hep/groot/rtree" 16 "go-hep.org/x/hep/hbook" 17 ) 18 19 // Scan executes a query against the given tree and runs the function f 20 // within that context. 21 func Scan(tree rtree.Tree, query string, f any) error { 22 if f == nil { 23 return fmt.Errorf("groot/rsql: nil func") 24 } 25 rv := reflect.ValueOf(f) 26 rt := rv.Type() 27 if rt.Kind() != reflect.Func { 28 return fmt.Errorf("groot/rsql: expected a func, got %T", f) 29 } 30 if rt.NumOut() != 1 || rt.Out(0) != reflect.TypeOf((*error)(nil)).Elem() { 31 return fmt.Errorf("groot/rsql: expected a func returning an error. got %T", f) 32 } 33 vargs := make([]reflect.Value, rt.NumIn()) 34 args := make([]any, rt.NumIn()) 35 for i := range args { 36 ptr := reflect.New(rt.In(i)) 37 args[i] = ptr.Interface() 38 vargs[i] = ptr.Elem() 39 } 40 41 db := rsqldrv.OpenDB(rtree.FileOf(tree)) 42 defer db.Close() 43 44 rows, err := db.Query(query) 45 if err != nil { 46 return err 47 } 48 defer rows.Close() 49 50 for rows.Next() { 51 err = rows.Scan(args...) 52 if err != nil { 53 return err 54 } 55 56 out := rv.Call(vargs)[0].Interface() 57 if out != nil { 58 return out.(error) 59 } 60 } 61 62 err = rows.Err() 63 if err == io.EOF { 64 err = nil 65 } 66 return err 67 } 68 69 // ScanH1D executes a query against the tree and fills the histogram with 70 // the results of the query. 71 // If h is nil, a (100-bins, xmin, xmax+ULP) histogram is created, 72 // where xmin and xmax are inferred from the content of the underlying database. 73 func ScanH1D(tree rtree.Tree, query string, h *hbook.H1D) (*hbook.H1D, error) { 74 if h == nil { 75 var ( 76 xmin = +math.MaxFloat64 77 xmax = -math.MaxFloat64 78 ) 79 // FIXME(sbinet) leverage the underlying db min/max functions, 80 // instead of crawling through the whole data set. 81 err := Scan(tree, query, func(x float64) error { 82 xmin = math.Min(xmin, x) 83 xmax = math.Max(xmax, x) 84 return nil 85 }) 86 if err != nil { 87 return nil, err 88 } 89 90 h = hbook.NewH1D(100, xmin, nextULP(xmax)) 91 } 92 93 err := Scan(tree, query, func(x float64) error { 94 h.Fill(x, 1) 95 return nil 96 }) 97 98 return h, err 99 } 100 101 // ScanH2D executes a query against the ntuple and fills the histogram with 102 // the results of the query. 103 // If h is nil, a (100-bins, xmin, xmax+ULP) (100-bins, ymin, ymax+ULP) 2d-histogram 104 // is created, 105 // where xmin, xmax and ymin,ymax are inferred from the content of the 106 // underlying database. 107 func ScanH2D(tree rtree.Tree, query string, h *hbook.H2D) (*hbook.H2D, error) { 108 if h == nil { 109 var ( 110 xmin = +math.MaxFloat64 111 xmax = -math.MaxFloat64 112 ymin = +math.MaxFloat64 113 ymax = -math.MaxFloat64 114 ) 115 // FIXME(sbinet) leverage the underlying db min/max functions, 116 // instead of crawling through the whole data set. 117 err := Scan(tree, query, func(x, y float64) error { 118 xmin = math.Min(xmin, x) 119 xmax = math.Max(xmax, x) 120 ymin = math.Min(ymin, y) 121 ymax = math.Max(ymax, y) 122 return nil 123 }) 124 if err != nil { 125 return nil, err 126 } 127 128 h = hbook.NewH2D(100, xmin, nextULP(xmax), 100, ymin, nextULP(ymax)) 129 } 130 131 err := Scan(tree, query, func(x, y float64) error { 132 h.Fill(x, y, 1) 133 return nil 134 }) 135 136 return h, err 137 } 138 139 func nextULP(v float64) float64 { 140 return math.Nextafter(v, v+1) 141 }