go-hep.org/x/hep@v0.38.1/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  		test := test
   250  		t.Run(test.name, func(t *testing.T) {
   251  			t.Parallel()
   252  
   253  			particles, err := loadParticles(test.input)
   254  			if err != nil {
   255  				t.Fatal(err)
   256  			}
   257  
   258  			cs, err := fastjet.NewClusterSequence(particles, test.def)
   259  			if err != nil {
   260  				t.Fatalf("error for jet definition: %v", err)
   261  			}
   262  
   263  			jets, err := cs.InclusiveJets(test.ptmin)
   264  			if err != nil {
   265  				t.Fatalf("incl-jets error: %v", err)
   266  			}
   267  
   268  			sort.Sort(fastjet.ByPt(jets))
   269  
   270  			want, err := loadRef("testdata/" + test.name + ".ref")
   271  			if err != nil {
   272  				t.Fatalf("error reading reference file: %v", err)
   273  			}
   274  
   275  			if len(want) != len(jets) {
   276  				t.Fatalf("got %d jets, want %d", len(jets), len(want))
   277  			}
   278  
   279  			n := min(len(want), len(jets))
   280  			for i := range n {
   281  				ref := want[i][:]
   282  				jet := jets[i]
   283  				rap := jet.Rapidity()
   284  				phi := angle0to2Pi(jet.Phi())
   285  				pt := jet.Pt()
   286  
   287  				got := []float64{rap, phi, pt}
   288  				if !floats.EqualApprox(got, ref, tol) {
   289  					t.Errorf("#%d\ngot= %v\nwant=%v", i, got, ref)
   290  				}
   291  			}
   292  		})
   293  	}
   294  }
   295  
   296  func TestExclusiveJetAlgorithms(t *testing.T) {
   297  	const tol = 1e-6
   298  
   299  	for _, test := range []struct {
   300  		input string
   301  		name  string
   302  		def   fastjet.JetDefinition
   303  		dcut  float64
   304  	}{
   305  		{
   306  			input: "testdata/single-ee-event.dat",
   307  			name:  "eekt_excld+2.0_r0.4_escheme_best",
   308  			def: fastjet.NewJetDefinitionExtra(
   309  				fastjet.EeKtAlgorithm, 0.4, fastjet.EScheme, fastjet.BestStrategy, 1,
   310  			),
   311  			dcut: 2.0,
   312  		},
   313  	} {
   314  		test := test
   315  		t.Run(test.name, func(t *testing.T) {
   316  			t.Parallel()
   317  
   318  			particles, err := loadParticles(test.input)
   319  			if err != nil {
   320  				t.Fatal(err)
   321  			}
   322  
   323  			cs, err := fastjet.NewClusterSequence(particles, test.def)
   324  			if err != nil {
   325  				t.Fatalf("error for jet definition: %v", err)
   326  			}
   327  
   328  			jets, err := cs.ExclusiveJets(test.dcut)
   329  			if err != nil {
   330  				t.Fatalf("incl-jets error: %v", err)
   331  			}
   332  
   333  			sort.Sort(fastjet.ByPt(jets))
   334  
   335  			want, err := loadRef("testdata/" + test.name + ".ref")
   336  			if err != nil {
   337  				t.Fatalf("error reading reference file: %v", err)
   338  			}
   339  
   340  			if len(want) != len(jets) {
   341  				t.Fatalf("got %d jets, want %d", len(jets), len(want))
   342  			}
   343  
   344  			n := min(len(want), len(jets))
   345  			for i := range n {
   346  				ref := want[i][:]
   347  				jet := jets[i]
   348  				rap := jet.Rapidity()
   349  				phi := angle0to2Pi(jet.Phi())
   350  				pt := jet.Pt()
   351  
   352  				got := []float64{rap, phi, pt}
   353  				if !floats.EqualApprox(got, ref, tol) {
   354  					t.Errorf("#%d\ngot= %v\nwant=%v", i, got, ref)
   355  				}
   356  			}
   357  		})
   358  	}
   359  }
   360  
   361  func loadParticles(name string) ([]fastjet.Jet, error) {
   362  	f, err := os.Open(name)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  	defer f.Close()
   367  
   368  	var particles []fastjet.Jet
   369  	scan := bufio.NewScanner(f)
   370  	for scan.Scan() {
   371  		txt := scan.Text()
   372  		toks := make([]string, 0, 4)
   373  		for _, tok := range strings.Split(txt, " ") {
   374  			if tok != "" {
   375  				toks = append(toks, tok)
   376  			}
   377  		}
   378  		px, err := strconv.ParseFloat(toks[0], 64)
   379  		if err != nil {
   380  			return nil, err
   381  		}
   382  		py, err := strconv.ParseFloat(toks[1], 64)
   383  		if err != nil {
   384  			return nil, err
   385  		}
   386  		pz, err := strconv.ParseFloat(toks[2], 64)
   387  		if err != nil {
   388  			return nil, err
   389  		}
   390  		e, err := strconv.ParseFloat(toks[3], 64)
   391  		if err != nil {
   392  			return nil, err
   393  		}
   394  		particles = append(particles, fastjet.NewJet(px, py, pz, e))
   395  	}
   396  	err = scan.Err()
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  
   401  	return particles, nil
   402  }
   403  
   404  func loadRef(name string) ([][3]float64, error) {
   405  	f, err := os.Open(name)
   406  	if err != nil {
   407  		return nil, err
   408  	}
   409  	defer f.Close()
   410  
   411  	var refs [][3]float64
   412  	scan := bufio.NewScanner(f)
   413  	for scan.Scan() {
   414  		var i int
   415  		var ref [3]float64
   416  		_, err = fmt.Sscanf(scan.Text(), "%5d %f %f %f", &i, &ref[0], &ref[1], &ref[2])
   417  		if err != nil {
   418  			return nil, err
   419  		}
   420  		refs = append(refs, ref)
   421  	}
   422  	err = scan.Err()
   423  	if err != nil {
   424  		return nil, err
   425  	}
   426  	return refs, nil
   427  }
   428  
   429  const twoPi = 2 * math.Pi
   430  
   431  func angle0to2Pi(v float64) float64 {
   432  	v = math.Mod(v, twoPi)
   433  	if v == 0 {
   434  		return 0
   435  	}
   436  	if v < 0 {
   437  		v += twoPi
   438  	}
   439  	if v == twoPi {
   440  		v = 0
   441  	}
   442  	return v
   443  }