go-hep.org/x/hep@v0.40.0/fastjet/algorithm_test.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 fastjet_test
     6  
     7  import (
     8  	"bufio"
     9  	"fmt"
    10  	"math"
    11  	"os"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  	"testing"
    16  
    17  	"go-hep.org/x/hep/fastjet"
    18  	"gonum.org/v1/gonum/floats"
    19  )
    20  
    21  func TestInclusiveJetAlgorithms(t *testing.T) {
    22  	const tol = 1e-6
    23  
    24  	for _, test := range []struct {
    25  		input string
    26  		name  string
    27  		def   fastjet.JetDefinition
    28  		ptmin float64
    29  	}{
    30  		{
    31  			input: "testdata/single-pp-event.dat",
    32  			name:  "antikt_r0.4_escheme_best",
    33  			def: fastjet.NewJetDefinition(
    34  				fastjet.AntiKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy,
    35  			),
    36  			ptmin: 0,
    37  		},
    38  		{
    39  			input: "testdata/single-pp-event.dat",
    40  			name:  "antikt_r0.7_escheme_best",
    41  			def: fastjet.NewJetDefinition(
    42  				fastjet.AntiKtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy,
    43  			),
    44  			ptmin: 0,
    45  		},
    46  		{
    47  			input: "testdata/single-pp-event.dat",
    48  			name:  "antikt_r1.0_escheme_best",
    49  			def: fastjet.NewJetDefinition(
    50  				fastjet.AntiKtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy,
    51  			),
    52  			ptmin: 0,
    53  		},
    54  		{
    55  			input: "testdata/single-pp-event.dat",
    56  			name:  "kt_r0.4_escheme_best",
    57  			def: fastjet.NewJetDefinition(
    58  				fastjet.KtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy,
    59  			),
    60  			ptmin: 0,
    61  		},
    62  		{
    63  			input: "testdata/single-pp-event.dat",
    64  			name:  "kt_r0.7_escheme_best",
    65  			def: fastjet.NewJetDefinition(
    66  				fastjet.KtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy,
    67  			),
    68  			ptmin: 0,
    69  		},
    70  		{
    71  			input: "testdata/single-pp-event.dat",
    72  			name:  "kt_r1.0_escheme_best",
    73  			def: fastjet.NewJetDefinition(
    74  				fastjet.KtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy,
    75  			),
    76  			ptmin: 0,
    77  		},
    78  		{
    79  			input: "testdata/single-pp-event.dat",
    80  			name:  "cam_r0.4_escheme_best",
    81  			def: fastjet.NewJetDefinition(
    82  				fastjet.CambridgeAachenAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy,
    83  			),
    84  			ptmin: 0,
    85  		},
    86  		{
    87  			input: "testdata/single-pp-event.dat",
    88  			name:  "cam_r0.7_escheme_best",
    89  			def: fastjet.NewJetDefinition(
    90  				fastjet.CambridgeAachenAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy,
    91  			),
    92  			ptmin: 0,
    93  		},
    94  		{
    95  			input: "testdata/single-pp-event.dat",
    96  			name:  "cam_r1.0_escheme_best",
    97  			def: fastjet.NewJetDefinition(
    98  				fastjet.CambridgeAachenAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy,
    99  			),
   100  			ptmin: 0,
   101  		},
   102  		{
   103  			input: "testdata/single-pp-event.dat",
   104  			name:  "genkt_p-1.0_r0.4_escheme_best",
   105  			def: fastjet.NewJetDefinitionExtra(
   106  				fastjet.GenKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, -1,
   107  			),
   108  			ptmin: 0,
   109  		},
   110  		{
   111  			input: "testdata/single-pp-event.dat",
   112  			name:  "genkt_p+0.0_r0.4_escheme_best",
   113  			def: fastjet.NewJetDefinitionExtra(
   114  				fastjet.GenKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, 0,
   115  			),
   116  			ptmin: 0,
   117  		},
   118  		{
   119  			input: "testdata/single-pp-event.dat",
   120  			name:  "genkt_p+1.0_r0.4_escheme_best",
   121  			def: fastjet.NewJetDefinitionExtra(
   122  				fastjet.GenKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, 1,
   123  			),
   124  			ptmin: 0,
   125  		},
   126  		{
   127  			input: "testdata/single-pp-event.dat",
   128  			name:  "genkt_p-1.0_r0.7_escheme_best",
   129  			def: fastjet.NewJetDefinitionExtra(
   130  				fastjet.GenKtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy, -1,
   131  			),
   132  			ptmin: 0,
   133  		},
   134  		{
   135  			input: "testdata/single-pp-event.dat",
   136  			name:  "genkt_p+0.0_r0.7_escheme_best",
   137  			def: fastjet.NewJetDefinitionExtra(
   138  				fastjet.GenKtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy, 0,
   139  			),
   140  			ptmin: 0,
   141  		},
   142  		{
   143  			input: "testdata/single-pp-event.dat",
   144  			name:  "genkt_p+1.0_r0.7_escheme_best",
   145  			def: fastjet.NewJetDefinitionExtra(
   146  				fastjet.GenKtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy, 1,
   147  			),
   148  			ptmin: 0,
   149  		},
   150  		{
   151  			input: "testdata/single-pp-event.dat",
   152  			name:  "genkt_p-1.0_r1.0_escheme_best",
   153  			def: fastjet.NewJetDefinitionExtra(
   154  				fastjet.GenKtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy, -1,
   155  			),
   156  			ptmin: 0,
   157  		},
   158  		{
   159  			input: "testdata/single-pp-event.dat",
   160  			name:  "genkt_p+0.0_r1.0_escheme_best",
   161  			def: fastjet.NewJetDefinitionExtra(
   162  				fastjet.GenKtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy, 0,
   163  			),
   164  			ptmin: 0,
   165  		},
   166  		{
   167  			input: "testdata/single-pp-event.dat",
   168  			name:  "genkt_p+1.0_r1.0_escheme_best",
   169  			def: fastjet.NewJetDefinitionExtra(
   170  				fastjet.GenKtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy, 1,
   171  			),
   172  			ptmin: 0,
   173  		},
   174  
   175  		// e+e- algs
   176  		{
   177  			input: "testdata/single-ee-event.dat",
   178  			name:  "eegenkt_p-1.0_r0.4_escheme_best",
   179  			def: fastjet.NewJetDefinitionExtra(
   180  				fastjet.EeGenKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, -1,
   181  			),
   182  			ptmin: 0,
   183  		},
   184  		{
   185  			input: "testdata/single-ee-event.dat",
   186  			name:  "eegenkt_p+0.0_r0.4_escheme_best",
   187  			def: fastjet.NewJetDefinitionExtra(
   188  				fastjet.EeGenKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, 0,
   189  			),
   190  			ptmin: 0,
   191  		},
   192  		{
   193  			input: "testdata/single-ee-event.dat",
   194  			name:  "eegenkt_p+1.0_r0.4_escheme_best",
   195  			def: fastjet.NewJetDefinitionExtra(
   196  				fastjet.EeGenKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, 1,
   197  			),
   198  			ptmin: 0,
   199  		},
   200  		{
   201  			input: "testdata/single-ee-event.dat",
   202  			name:  "eegenkt_p-1.0_r0.7_escheme_best",
   203  			def: fastjet.NewJetDefinitionExtra(
   204  				fastjet.EeGenKtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy, -1,
   205  			),
   206  			ptmin: 0,
   207  		},
   208  		{
   209  			input: "testdata/single-ee-event.dat",
   210  			name:  "eegenkt_p+0.0_r0.7_escheme_best",
   211  			def: fastjet.NewJetDefinitionExtra(
   212  				fastjet.EeGenKtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy, 0,
   213  			),
   214  			ptmin: 0,
   215  		},
   216  		{
   217  			input: "testdata/single-ee-event.dat",
   218  			name:  "eegenkt_p+1.0_r0.7_escheme_best",
   219  			def: fastjet.NewJetDefinitionExtra(
   220  				fastjet.EeGenKtAlgorithm, 0.7, fastjet.EScheme, fastjet.BestStrategy, 1,
   221  			),
   222  			ptmin: 0,
   223  		},
   224  		{
   225  			input: "testdata/single-ee-event.dat",
   226  			name:  "eegenkt_p-1.0_r1.0_escheme_best",
   227  			def: fastjet.NewJetDefinitionExtra(
   228  				fastjet.EeGenKtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy, -1,
   229  			),
   230  			ptmin: 0,
   231  		},
   232  		{
   233  			input: "testdata/single-ee-event.dat",
   234  			name:  "eegenkt_p+0.0_r1.0_escheme_best",
   235  			def: fastjet.NewJetDefinitionExtra(
   236  				fastjet.EeGenKtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy, 0,
   237  			),
   238  			ptmin: 0,
   239  		},
   240  		{
   241  			input: "testdata/single-ee-event.dat",
   242  			name:  "eegenkt_p+1.0_r1.0_escheme_best",
   243  			def: fastjet.NewJetDefinitionExtra(
   244  				fastjet.EeGenKtAlgorithm, 1.0, fastjet.EScheme, fastjet.BestStrategy, 1,
   245  			),
   246  			ptmin: 0,
   247  		},
   248  	} {
   249  		t.Run(test.name, func(t *testing.T) {
   250  			t.Parallel()
   251  
   252  			particles, err := loadParticles(test.input)
   253  			if err != nil {
   254  				t.Fatal(err)
   255  			}
   256  
   257  			cs, err := fastjet.NewClusterSequence(particles, test.def)
   258  			if err != nil {
   259  				t.Fatalf("error for jet definition: %v", err)
   260  			}
   261  
   262  			jets, err := cs.InclusiveJets(test.ptmin)
   263  			if err != nil {
   264  				t.Fatalf("incl-jets error: %v", err)
   265  			}
   266  
   267  			sort.Sort(fastjet.ByPt(jets))
   268  
   269  			want, err := loadRef("testdata/" + test.name + ".ref")
   270  			if err != nil {
   271  				t.Fatalf("error reading reference file: %v", err)
   272  			}
   273  
   274  			if len(want) != len(jets) {
   275  				t.Fatalf("got %d jets, want %d", len(jets), len(want))
   276  			}
   277  
   278  			n := min(len(want), len(jets))
   279  			for i := range n {
   280  				ref := want[i][:]
   281  				jet := jets[i]
   282  				rap := jet.Rapidity()
   283  				phi := angle0to2Pi(jet.Phi())
   284  				pt := jet.Pt()
   285  
   286  				got := []float64{rap, phi, pt}
   287  				if !floats.EqualApprox(got, ref, tol) {
   288  					t.Errorf("#%d\ngot= %v\nwant=%v", i, got, ref)
   289  				}
   290  			}
   291  		})
   292  	}
   293  }
   294  
   295  func TestExclusiveJetAlgorithms(t *testing.T) {
   296  	const tol = 1e-6
   297  
   298  	for _, test := range []struct {
   299  		input string
   300  		name  string
   301  		def   fastjet.JetDefinition
   302  		dcut  float64
   303  	}{
   304  		{
   305  			input: "testdata/single-ee-event.dat",
   306  			name:  "eekt_excld+2.0_r0.4_escheme_best",
   307  			def: fastjet.NewJetDefinitionExtra(
   308  				fastjet.EeKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, 1,
   309  			),
   310  			dcut: 2.0,
   311  		},
   312  	} {
   313  		t.Run(test.name, func(t *testing.T) {
   314  			t.Parallel()
   315  
   316  			particles, err := loadParticles(test.input)
   317  			if err != nil {
   318  				t.Fatal(err)
   319  			}
   320  
   321  			cs, err := fastjet.NewClusterSequence(particles, test.def)
   322  			if err != nil {
   323  				t.Fatalf("error for jet definition: %v", err)
   324  			}
   325  
   326  			jets, err := cs.ExclusiveJets(test.dcut)
   327  			if err != nil {
   328  				t.Fatalf("incl-jets error: %v", err)
   329  			}
   330  
   331  			sort.Sort(fastjet.ByPt(jets))
   332  
   333  			want, err := loadRef("testdata/" + test.name + ".ref")
   334  			if err != nil {
   335  				t.Fatalf("error reading reference file: %v", err)
   336  			}
   337  
   338  			if len(want) != len(jets) {
   339  				t.Fatalf("got %d jets, want %d", len(jets), len(want))
   340  			}
   341  
   342  			n := min(len(want), len(jets))
   343  			for i := range n {
   344  				ref := want[i][:]
   345  				jet := jets[i]
   346  				rap := jet.Rapidity()
   347  				phi := angle0to2Pi(jet.Phi())
   348  				pt := jet.Pt()
   349  
   350  				got := []float64{rap, phi, pt}
   351  				if !floats.EqualApprox(got, ref, tol) {
   352  					t.Errorf("#%d\ngot= %v\nwant=%v", i, got, ref)
   353  				}
   354  			}
   355  		})
   356  	}
   357  }
   358  
   359  func loadParticles(name string) ([]fastjet.Jet, error) {
   360  	f, err := os.Open(name)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  	defer f.Close()
   365  
   366  	var particles []fastjet.Jet
   367  	scan := bufio.NewScanner(f)
   368  	for scan.Scan() {
   369  		txt := scan.Text()
   370  		toks := make([]string, 0, 4)
   371  		for tok := range strings.SplitSeq(txt, " ") {
   372  			if tok != "" {
   373  				toks = append(toks, tok)
   374  			}
   375  		}
   376  		px, err := strconv.ParseFloat(toks[0], 64)
   377  		if err != nil {
   378  			return nil, err
   379  		}
   380  		py, err := strconv.ParseFloat(toks[1], 64)
   381  		if err != nil {
   382  			return nil, err
   383  		}
   384  		pz, err := strconv.ParseFloat(toks[2], 64)
   385  		if err != nil {
   386  			return nil, err
   387  		}
   388  		e, err := strconv.ParseFloat(toks[3], 64)
   389  		if err != nil {
   390  			return nil, err
   391  		}
   392  		particles = append(particles, fastjet.NewJet(px, py, pz, e))
   393  	}
   394  	err = scan.Err()
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  
   399  	return particles, nil
   400  }
   401  
   402  func loadRef(name string) ([][3]float64, error) {
   403  	f, err := os.Open(name)
   404  	if err != nil {
   405  		return nil, err
   406  	}
   407  	defer f.Close()
   408  
   409  	var refs [][3]float64
   410  	scan := bufio.NewScanner(f)
   411  	for scan.Scan() {
   412  		var i int
   413  		var ref [3]float64
   414  		_, err = fmt.Sscanf(scan.Text(), "%5d %f %f %f", &i, &ref[0], &ref[1], &ref[2])
   415  		if err != nil {
   416  			return nil, err
   417  		}
   418  		refs = append(refs, ref)
   419  	}
   420  	err = scan.Err()
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  	return refs, nil
   425  }
   426  
   427  const twoPi = 2 * math.Pi
   428  
   429  func angle0to2Pi(v float64) float64 {
   430  	v = math.Mod(v, twoPi)
   431  	if v == 0 {
   432  		return 0
   433  	}
   434  	if v < 0 {
   435  		v += twoPi
   436  	}
   437  	if v == twoPi {
   438  		v = 0
   439  	}
   440  	return v
   441  }