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 }