go-hep.org/x/hep@v0.38.1/cmd/fits2root/main.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 // fits2root converts the content of a FITS table to a ROOT file and tree. 6 // 7 // Usage: fits2root [OPTIONS] -f input.fits 8 // 9 // Example: 10 // 11 // $> fits2root -f ./input.fits -t MyHDU 12 // 13 // Options: 14 // 15 // -f string 16 // path to input FITS file name 17 // -o string 18 // path to output ROOT file name (default "output.root") 19 // -t string 20 // name of the FITS table to convert 21 package main 22 23 import ( 24 "flag" 25 "fmt" 26 "log" 27 "os" 28 "reflect" 29 30 "codeberg.org/astrogo/fitsio" 31 "go-hep.org/x/hep/groot" 32 "go-hep.org/x/hep/groot/rtree" 33 ) 34 35 func main() { 36 log.SetPrefix("fits2root: ") 37 log.SetFlags(0) 38 39 fname := flag.String("f", "", "path to input FITS file name") 40 oname := flag.String("o", "output.root", "path to output ROOT file name") 41 tname := flag.String("t", "", "name of the FITS table to convert") 42 43 flag.Usage = func() { 44 fmt.Printf(`fits2root converts the content of a FITS table to a ROOT file and tree. 45 46 Usage: fits2root [OPTIONS] -f input.fits 47 48 Example: 49 50 $> fits2root -f ./input.fits -t MyHDU 51 52 Options: 53 `) 54 flag.PrintDefaults() 55 } 56 57 flag.Parse() 58 59 if *fname == "" { 60 flag.Usage() 61 log.Fatalf("missing path to input FITS file argument") 62 } 63 64 err := process(*oname, *tname, *fname) 65 if err != nil { 66 log.Fatalf("%+v", err) 67 } 68 } 69 70 func process(oname, tname, fname string) error { 71 f, err := os.Open(fname) 72 if err != nil { 73 return fmt.Errorf("could not open input file %q: %w", fname, err) 74 } 75 defer f.Close() 76 77 fits, err := fitsio.Open(f) 78 if err != nil { 79 return fmt.Errorf("could not open FITS file %q: %w", fname, err) 80 } 81 defer fits.Close() 82 83 hdu := fits.Get(tname) 84 if hdu == nil { 85 return fmt.Errorf("could not retrieve HDU %q from FITS file %q", tname, fname) 86 } 87 defer hdu.Close() 88 89 tbl, ok := hdu.(*fitsio.Table) 90 if !ok { 91 return fmt.Errorf("HDU %q from FITS file %q is not a Table", tname, fname) 92 } 93 94 o, err := groot.Create(oname) 95 if err != nil { 96 return fmt.Errorf("could not create output ROOT file %q: %w", oname, err) 97 } 98 defer o.Close() 99 100 var ( 101 wvars = make([]rtree.WriteVar, tbl.NumCols()) 102 wargs = make([]any, tbl.NumCols()) 103 ) 104 for i, col := range tbl.Cols() { 105 wvars[i] = wvarFrom(col) 106 wargs[i] = wvars[i].Value 107 } 108 109 if tname == "" { 110 tname = "FITS" 111 } 112 113 tree, err := rtree.NewWriter(o, tname, wvars) 114 if err != nil { 115 return fmt.Errorf("could not create output ROOT tree %q: %w", tname, err) 116 } 117 118 rows, err := tbl.Read(0, tbl.NumRows()) 119 if err != nil { 120 return fmt.Errorf("could not read FITS table range [0, %d): %w", tbl.NumRows(), err) 121 } 122 defer rows.Close() 123 124 var irow int64 125 for rows.Next() { 126 err = rows.Scan(wargs...) 127 if err != nil { 128 return fmt.Errorf("could not read row %d: %w", irow, err) 129 } 130 131 _, err = tree.Write() 132 if err != nil { 133 return fmt.Errorf("could not write row %d: %w", irow, err) 134 } 135 136 irow++ 137 } 138 139 err = tree.Close() 140 if err != nil { 141 return fmt.Errorf("could not close ROOT tree writer: %w", err) 142 } 143 144 err = o.Close() 145 if err != nil { 146 return fmt.Errorf("could not close output ROOT file %q: %w", oname, err) 147 } 148 149 return nil 150 } 151 152 func wvarFrom(col fitsio.Column) rtree.WriteVar { 153 rt := col.Type() 154 switch rt.Kind() { 155 case reflect.Bool: 156 return rtree.WriteVar{ 157 Name: col.Name, 158 Value: new(bool), 159 } 160 161 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 162 return rtree.WriteVar{ 163 Name: col.Name, 164 Value: reflect.New(rt).Interface(), 165 } 166 167 case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 168 return rtree.WriteVar{ 169 Name: col.Name, 170 Value: reflect.New(rt).Interface(), 171 } 172 173 case reflect.Float32, reflect.Float64: 174 return rtree.WriteVar{ 175 Name: col.Name, 176 Value: reflect.New(rt).Interface(), 177 } 178 179 case reflect.String: 180 return rtree.WriteVar{ 181 Name: col.Name, 182 Value: new(string), 183 } 184 185 case reflect.Array: 186 return rtree.WriteVar{ 187 Name: col.Name, 188 Value: reflect.New(rt).Interface(), 189 } 190 191 default: 192 panic(fmt.Errorf("fits2root: invalid column type %+v (kind=%v)", col, rt.Kind())) 193 } 194 }