go-hep.org/x/hep@v0.38.1/slha/decode.go (about)

     1  // Copyright ©2017 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 slha
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"math"
    13  	"os"
    14  	"reflect"
    15  	"regexp"
    16  	"strconv"
    17  	"strings"
    18  )
    19  
    20  var (
    21  	reBlock = regexp.MustCompile(`BLOCK\s+(\w+)(\s+Q\s*=\s*.+)?`)
    22  	reDecay = regexp.MustCompile(`DECAY\s+(-?\d+)\s+([\d\.E+-]+|NAN).*`)
    23  )
    24  
    25  // Decode reads SLHA informations from r and returns them as a *slha.SLHA.
    26  func Decode(r io.Reader) (*SLHA, error) {
    27  	var err error
    28  	type stateType int
    29  	const (
    30  		stBlock stateType = 1
    31  		stDecay stateType = 2
    32  	)
    33  	var state stateType
    34  	var blk *Block
    35  	var part *Particle
    36  	var data SLHA
    37  	scan := bufio.NewScanner(r)
    38  	for scan.Scan() {
    39  		bline := scan.Bytes()
    40  		if len(bline) <= 0 {
    41  			continue
    42  		}
    43  		if bline[0] == '#' {
    44  			// TODO(sbinet) store block/entry comments
    45  			continue
    46  		}
    47  		bup := bytes.ToUpper(bline)
    48  		switch bup[0] {
    49  		case 'B':
    50  			idx := bytes.Index(bup, []byte("#"))
    51  			comment := ""
    52  			if idx > 0 {
    53  				comment = strings.TrimSpace(string(bline[idx+1:]))
    54  				bup = bytes.TrimSpace(bup[:idx])
    55  			}
    56  			// fmt.Printf("Block> %q (comment=%q)\n", string(bup), comment)
    57  			state = stBlock
    58  			all := reBlock.FindStringSubmatch(string(bup))
    59  			if all == nil {
    60  				return nil, fmt.Errorf("slha.decode: invalid block: %q", string(bline))
    61  			}
    62  			groups := all[1:]
    63  			// for i, v := range groups {
    64  			// 	fmt.Printf("  %d/%d: %q\n", i+1, len(groups), v)
    65  			// }
    66  			i := len(data.Blocks)
    67  			data.Blocks = append(data.Blocks, Block{
    68  				Name:    groups[0],
    69  				Comment: comment,
    70  				Q:       math.NaN(),
    71  				Data:    make(DataArray, 0),
    72  			})
    73  			blk = &data.Blocks[i]
    74  			if len(groups) > 1 && groups[1] != "" {
    75  				qstr := groups[1]
    76  				idx := strings.Index(qstr, "=")
    77  				if idx > 0 {
    78  					qstr = strings.TrimSpace(qstr[idx+1:])
    79  				}
    80  				blk.Q, err = strconv.ParseFloat(qstr, 64)
    81  				if err != nil {
    82  					return nil, err
    83  				}
    84  			}
    85  			// fmt.Printf("Block> %v\n", blk)
    86  
    87  		case 'D':
    88  			state = stDecay
    89  			idx := bytes.Index(bup, []byte("#"))
    90  			comment := ""
    91  			if idx > 0 {
    92  				comment = strings.TrimSpace(string(bline[idx+1:]))
    93  				bup = bytes.TrimSpace(bup[:idx])
    94  			}
    95  			all := reDecay.FindStringSubmatch(string(bup))
    96  			if all == nil {
    97  				return nil, fmt.Errorf("slha.decode: invalid decay: %q", string(bline))
    98  			}
    99  			groups := all[1:]
   100  			pdgid, err := strconv.Atoi(groups[0])
   101  			if err != nil {
   102  				return nil, err
   103  			}
   104  			width := math.NaN()
   105  			if len(groups) > 1 && groups[1] != "" {
   106  				width, err = strconv.ParseFloat(groups[1], 64)
   107  				if err != nil {
   108  					return nil, err
   109  				}
   110  			}
   111  			i := len(data.Particles)
   112  			data.Particles = append(data.Particles, Particle{
   113  				PdgID:   pdgid,
   114  				Width:   width,
   115  				Mass:    math.NaN(),
   116  				Comment: comment,
   117  				Decays:  make(Decays, 0, 2),
   118  			})
   119  			part = &data.Particles[i]
   120  
   121  		case '\t', ' ':
   122  			// data line
   123  			switch state {
   124  			case stBlock:
   125  				err = addBlockEntry(bline, blk)
   126  				if err != nil {
   127  					return nil, err
   128  				}
   129  			case stDecay:
   130  				err = addDecayEntry(bline, part)
   131  				if err != nil {
   132  					return nil, err
   133  				}
   134  			}
   135  		default:
   136  
   137  			fmt.Fprintf(os.Stderr, "**WARN** ignoring unknown section [%s]\n", string(bup))
   138  		}
   139  	}
   140  	err = scan.Err()
   141  	if err != nil {
   142  		if err != io.EOF {
   143  			return nil, err
   144  		}
   145  		err = nil
   146  	}
   147  
   148  	// try to populate particles' masses from the MASS block
   149  	if blk := data.Blocks.Get("MASS"); blk != nil {
   150  		for i := range data.Particles {
   151  			part := &data.Particles[i]
   152  			pdgid := part.PdgID
   153  			val, err := blk.Get(pdgid)
   154  			if err == nil {
   155  				part.Mass = val.Float()
   156  			}
   157  		}
   158  	}
   159  	return &data, err
   160  }
   161  
   162  func addBlockEntry(line []byte, blk *Block) error {
   163  	var err error
   164  	var val Value
   165  	hidx := bytes.Index(line, []byte("#"))
   166  	if hidx > 0 {
   167  		val.c = strings.TrimSpace(string(line[hidx+1:]))
   168  		line = line[:hidx]
   169  	}
   170  	line = bytes.TrimSpace(line)
   171  	tokens := make([][]byte, 0, 3)
   172  	for _, tok := range bytes.Split(line, []byte(" ")) {
   173  		if len(tok) <= 0 || bytes.Equal(tok, []byte("")) {
   174  			continue
   175  		}
   176  		tokens = append(tokens, tok)
   177  	}
   178  
   179  	// switch blk.Name {
   180  	// case "DCINFO":
   181  	// 	tokens
   182  	// }
   183  
   184  	ntokens := len(tokens) - 1
   185  	index := make([]int, ntokens)
   186  	for i := range index {
   187  		tok := string(tokens[i])
   188  		index[i], err = strconv.Atoi(tok)
   189  		if err != nil {
   190  			return fmt.Errorf("slha.decode: invalid index %q. err=%v", tok, err)
   191  		}
   192  	}
   193  
   194  	sval := string(tokens[len(index)])
   195  	switch blk.Name {
   196  	case "MODSEL":
   197  		v, err := strconv.Atoi(sval)
   198  		if err != nil {
   199  			return err
   200  		}
   201  		val.v = reflect.ValueOf(v)
   202  
   203  	case "SPINFO", "DCINFO":
   204  		val.v = reflect.ValueOf(sval)
   205  
   206  	default:
   207  		v, err := anyvalue(sval)
   208  		if err != nil {
   209  			return err
   210  		}
   211  		val.v = reflect.ValueOf(v)
   212  	}
   213  
   214  	// fmt.Printf("--- %q (comment=%q) len=%d indices=%v val=%#v\n", string(line), val.c, len(tokens), index, val.Interface())
   215  
   216  	blk.Data = append(blk.Data, DataItem{
   217  		Index: NewIndex(index...),
   218  		Value: val,
   219  	})
   220  	return err
   221  }
   222  
   223  func anyvalue(str string) (any, error) {
   224  	var err error
   225  	var vv any
   226  
   227  	vfloat, err := strconv.ParseFloat(str, 64)
   228  	if err == nil {
   229  		vv = reflect.ValueOf(vfloat).Interface()
   230  		return vv, err
   231  	}
   232  
   233  	if strings.Count(str, "D") == 1 {
   234  		vfloat, err = strconv.ParseFloat(strings.Replace(str, "D", "E", 1), 64)
   235  		if err == nil {
   236  			vv = reflect.ValueOf(vfloat).Interface()
   237  			return vv, err
   238  		}
   239  	}
   240  
   241  	if strings.Count(str, "d") == 1 {
   242  		vfloat, err = strconv.ParseFloat(strings.Replace(str, "d", "E", 1), 64)
   243  		if err == nil {
   244  			vv = reflect.ValueOf(vfloat).Interface()
   245  			return vv, err
   246  		}
   247  	}
   248  
   249  	vint, err := strconv.Atoi(str)
   250  	if err == nil {
   251  		vv = reflect.ValueOf(int64(vint)).Interface()
   252  		return vv, err
   253  	}
   254  
   255  	vv = str
   256  	return vv, err
   257  }
   258  
   259  func addDecayEntry(line []byte, part *Particle) error {
   260  	var err error
   261  	comment := ""
   262  	hidx := bytes.Index(line, []byte("#"))
   263  	if hidx > 0 {
   264  		comment = strings.TrimSpace(string(line[hidx+1:]))
   265  		line = line[:hidx]
   266  	}
   267  	line = bytes.TrimSpace(line)
   268  	tokens := make([][]byte, 0, 3)
   269  	for _, tok := range bytes.Split(line, []byte(" ")) {
   270  		if len(tok) <= 0 || bytes.Equal(tok, []byte("")) {
   271  			continue
   272  		}
   273  		tokens = append(tokens, tok)
   274  	}
   275  	br, err := strconv.ParseFloat(string(tokens[0]), 64)
   276  	if err != nil {
   277  		return fmt.Errorf("slha.decode: invalid decay line %q. err=%v", string(line), err)
   278  	}
   279  	nda, err := strconv.Atoi(string(tokens[1]))
   280  	if err != nil {
   281  		return fmt.Errorf("slha.decode: invalid decay line %q. err=%v", string(line), err)
   282  	}
   283  	ids := make([]int, nda)
   284  	toks := tokens[2:]
   285  	for i := range ids {
   286  		ids[i], err = strconv.Atoi(string(toks[i]))
   287  		if err != nil {
   288  			return fmt.Errorf("slha.decode: invalid decay line %q. err=%v", string(line), err)
   289  		}
   290  	}
   291  	part.Decays = append(part.Decays, Decay{
   292  		Br:      br,
   293  		IDs:     ids,
   294  		Comment: comment,
   295  	})
   296  	// i := len(part.Decays) - 1
   297  	// fmt.Printf("--- %q (comment=%q) len=%d decay=%#v\n", string(line), comment, len(tokens), part.Decays[i])
   298  	return err
   299  }