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 }