go-hep.org/x/hep@v0.38.1/hepmc/hepmc.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 hepmc is a pure Go implementation of the C++ HepMC-2 library. 6 package hepmc // import "go-hep.org/x/hep/hepmc" 7 8 import ( 9 "errors" 10 "fmt" 11 "io" 12 "sort" 13 14 "go-hep.org/x/hep/fmom" 15 ) 16 17 var ( 18 errNilVtx = errors.New("hepmc: nil Vertex") 19 errNilParticle = errors.New("hepmc: nil Particle") 20 ) 21 22 // Delete deletes an event and allows memory to be reclaimed by the garbage collector 23 func Delete(evt *Event) error { 24 var err error 25 if evt == nil { 26 return err 27 } 28 if evt.SignalVertex != nil { 29 evt.SignalVertex.Event = nil 30 } 31 evt.SignalVertex = nil 32 evt.Beams[0] = nil 33 evt.Beams[1] = nil 34 35 for _, p := range evt.Particles { 36 p.ProdVertex = nil 37 p.EndVertex = nil 38 p.Flow.Particle = nil 39 delete(evt.Particles, p.Barcode) 40 } 41 for _, vtx := range evt.Vertices { 42 vtx.Event = nil 43 vtx.ParticlesIn = nil 44 vtx.ParticlesOut = nil 45 delete(evt.Vertices, vtx.Barcode) 46 } 47 48 evt.Particles = nil 49 evt.Vertices = nil 50 return err 51 } 52 53 // Event represents a record for MC generators (for use at any stage of generation) 54 // 55 // This type is intended as both a "container class" ( to store a MC 56 // event for interface between MC generators and detector simulation ) 57 // and also as a "work in progress class" ( that could be used inside 58 // a generator and modified as the event is built ). 59 type Event struct { 60 SignalProcessID int // id of the signal process 61 EventNumber int // event number 62 Mpi int // number of multi particle interactions 63 Scale float64 // energy scale, 64 AlphaQCD float64 // QCD coupling, see hep-ph/0109068 65 AlphaQED float64 // QED coupling, see hep-ph/0109068 66 67 SignalVertex *Vertex // signal vertex 68 Beams [2]*Particle // incoming beams 69 Weights Weights // weights for this event. first weight is used by default for hit and miss 70 RandomStates []int64 // container of random number generator states 71 72 Vertices map[int]*Vertex 73 Particles map[int]*Particle 74 75 CrossSection *CrossSection 76 HeavyIon *HeavyIon 77 PdfInfo *PdfInfo 78 MomentumUnit MomentumUnit 79 LengthUnit LengthUnit 80 81 bcparts int // barcode suggestions for particles 82 bcverts int // barcode suggestions for vertices 83 } 84 85 // Delete prepares this event for GC-reclaim 86 func (evt *Event) Delete() error { 87 return Delete(evt) 88 } 89 90 // AddVertex adds a vertex to this event 91 func (evt *Event) AddVertex(vtx *Vertex) error { 92 if vtx == nil { 93 return errNilVtx 94 } 95 //TODO(sbinet): warn and remove from previous event 96 //if vtx.Event != nil && vtx.Event != evt { 97 //} 98 return vtx.setParentEvent(evt) 99 } 100 101 // Print prints the event to w in a human-readable format 102 func (evt *Event) Print(w io.Writer) error { 103 var err error 104 const liner = ("________________________________________" + 105 "________________________________________") 106 107 _, err = fmt.Fprintf(w, "%s\n", liner) 108 if err != nil { 109 return err 110 } 111 112 sigVtx := 0 113 if evt.SignalVertex != nil { 114 sigVtx = evt.SignalVertex.Barcode 115 } 116 _, err = fmt.Fprintf( 117 w, 118 "GenEvent: #%04d ID=%5d SignalProcessGenVertex Barcode: %d\n", 119 evt.EventNumber, 120 evt.SignalProcessID, 121 sigVtx, 122 ) 123 if err != nil { 124 return err 125 } 126 _, err = fmt.Fprintf( 127 w, 128 " Momentum units:%8s Position units:%8s\n", 129 evt.MomentumUnit.String(), 130 evt.LengthUnit.String(), 131 ) 132 if err != nil { 133 return err 134 } 135 136 if evt.CrossSection != nil { 137 _, err = fmt.Fprintf( 138 w, 139 " Cross Section: %e +/- %e\n", 140 evt.CrossSection.Value, 141 evt.CrossSection.Error, 142 ) 143 if err != nil { 144 return err 145 } 146 } 147 148 _, err = fmt.Fprintf( 149 w, 150 " Entries this event: %d vertices, %d particles.\n", 151 len(evt.Vertices), 152 len(evt.Particles), 153 ) 154 if err != nil { 155 return err 156 } 157 158 if evt.Beams[0] != nil && evt.Beams[1] != nil { 159 _, err = fmt.Fprintf( 160 w, 161 " Beam Particle barcodes: %d %d \n", 162 evt.Beams[0].Barcode, 163 evt.Beams[1].Barcode, 164 ) 165 } else { 166 _, err = fmt.Fprintf( 167 w, 168 " Beam Particles are not defined.\n", 169 ) 170 } 171 if err != nil { 172 return err 173 } 174 175 // random state 176 _, err = fmt.Fprintf( 177 w, 178 " RndmState(%d)=", 179 len(evt.RandomStates), 180 ) 181 if err != nil { 182 return err 183 } 184 185 for _, rnd := range evt.RandomStates { 186 _, err = fmt.Fprintf(w, "%d ", rnd) 187 if err != nil { 188 return err 189 } 190 } 191 _, err = fmt.Fprintf(w, "\n") 192 if err != nil { 193 return err 194 } 195 196 // weights 197 _, err = fmt.Fprintf( 198 w, 199 " Wgts(%d)=", 200 len(evt.Weights.Map), 201 ) 202 if err != nil { 203 return err 204 } 205 206 for n := range evt.Weights.Map { 207 _, err = fmt.Fprintf(w, "(%s,%f) ", n, evt.Weights.At(n)) 208 if err != nil { 209 return err 210 } 211 } 212 _, err = fmt.Fprintf(w, "\n") 213 if err != nil { 214 return err 215 } 216 217 _, err = fmt.Fprintf( 218 w, 219 " EventScale %7.5f [energy] \t alphaQCD=%8.8f\t alphaQED=%8.8f\n", 220 evt.Scale, 221 evt.AlphaQCD, 222 evt.AlphaQED, 223 ) 224 if err != nil { 225 return err 226 } 227 228 // print a legend to describe the particle info 229 _, err = fmt.Fprintf( 230 w, 231 " GenParticle Legend\n"+ 232 " Barcode PDG ID ( Px, Py, Pz, E )"+ 233 " Stat DecayVtx\n") 234 if err != nil { 235 return err 236 } 237 238 _, err = fmt.Fprintf(w, "%s\n", liner) 239 if err != nil { 240 return err 241 } 242 243 // print all vertices 244 barcodes := make([]int, 0, len(evt.Vertices)) 245 for bc := range evt.Vertices { 246 barcodes = append(barcodes, -bc) 247 } 248 sort.Ints(barcodes) 249 for _, bc := range barcodes { 250 vtx := evt.Vertices[-bc] 251 err = vtx.Print(w) 252 if err != nil { 253 return err 254 } 255 } 256 _, err = fmt.Fprintf(w, "%s\n", liner) 257 if err != nil { 258 return err 259 } 260 return err 261 } 262 263 func (evt *Event) removeVertex(bc int) { 264 // TODO(sbinet) remove barcode from suggested vtx-barcodes ? 265 delete(evt.Vertices, bc) 266 } 267 268 func (evt *Event) removeParticle(bc int) { 269 // TODO(sbinet) remove barcode from suggested particle-barcodes ? 270 delete(evt.Particles, bc) 271 } 272 273 func (evt *Event) vBarcode() int { 274 // TODO(sbinet) make goroutine-safe 275 evt.bcverts++ 276 return -evt.bcverts 277 } 278 279 func (evt *Event) pBarcode() int { 280 // TODO(sbinet) make goroutine-safe 281 evt.bcparts++ 282 return evt.bcparts 283 } 284 285 // Particle represents a generator particle within an event coming in/out of a vertex 286 // 287 // Particle is the basic building block of the event record 288 type Particle struct { 289 Momentum fmom.PxPyPzE // momentum vector 290 PdgID int64 // id according to PDG convention 291 Status int // status code as defined for HEPEVT 292 Flow Flow // flow of this particle 293 Polarization Polarization // polarization of this particle 294 ProdVertex *Vertex // pointer to production vertex (nil if vacuum or beam) 295 EndVertex *Vertex // pointer to decay vertex (nil if not-decayed) 296 Barcode int // unique identifier in the event 297 GeneratedMass float64 // mass of this particle when it was generated 298 } 299 300 func (p *Particle) dump(w io.Writer) error { 301 var err error 302 _, err = fmt.Fprintf( 303 w, 304 " %9d%9d %+9.2e,%+9.2e,%+9.2e,%+9.2e", 305 p.Barcode, 306 p.PdgID, 307 p.Momentum.Px(), 308 p.Momentum.Py(), 309 p.Momentum.Pz(), 310 p.Momentum.E(), 311 ) 312 if err != nil { 313 return err 314 } 315 316 switch { 317 case p.EndVertex != nil && p.EndVertex.Barcode != 0: 318 _, err = fmt.Fprintf(w, "%4d %9d", p.Status, p.EndVertex.Barcode) 319 case p.EndVertex == nil: 320 _, err = fmt.Fprintf(w, "%4d", p.Status) 321 default: 322 _, err = fmt.Fprintf(w, "%4d %p", p.Status, p.EndVertex) 323 } 324 return err 325 } 326 327 // Particles is a []*Particle sorted by increasing-barcodes 328 type Particles []*Particle 329 330 func (ps Particles) Len() int { 331 return len(ps) 332 } 333 func (ps Particles) Less(i, j int) bool { 334 return ps[i].Barcode < ps[j].Barcode 335 } 336 func (ps Particles) Swap(i, j int) { 337 ps[i], ps[j] = ps[j], ps[i] 338 } 339 340 // Vertices is a []*Vertex sorted by increasing-barcodes 341 type Vertices []*Vertex 342 343 func (ps Vertices) Len() int { 344 return len(ps) 345 } 346 func (ps Vertices) Less(i, j int) bool { 347 return ps[i].Barcode < ps[j].Barcode 348 } 349 func (ps Vertices) Swap(i, j int) { 350 ps[i], ps[j] = ps[j], ps[i] 351 } 352 353 // Vertex represents a generator vertex within an event 354 // A vertex is indirectly (via particle "edges") linked to other 355 // 356 // vertices ("nodes") to form a composite "graph" 357 type Vertex struct { 358 Position fmom.PxPyPzE // 4-vector of vertex [mm] 359 ParticlesIn []*Particle // all incoming particles 360 ParticlesOut []*Particle // all outgoing particles 361 ID int // vertex id 362 Weights Weights // weights for this vertex 363 Event *Event // pointer to event owning this vertex 364 Barcode int // unique identifier in the event 365 } 366 367 func (vtx *Vertex) setParentEvent(evt *Event) error { 368 var err error 369 origEvt := vtx.Event 370 vtx.Event = evt 371 // if orig_evt == evt { 372 // return err 373 // } 374 if evt != nil { 375 if vtx.Barcode == 0 { 376 vtx.Barcode = evt.vBarcode() 377 } 378 evt.Vertices[vtx.Barcode] = vtx 379 } 380 if origEvt != nil && origEvt != evt { 381 origEvt.removeVertex(vtx.Barcode) 382 } 383 // we also need to loop over all the particles which are owned by 384 // this vertex and remove their barcodes from the old event. 385 for _, p := range vtx.ParticlesIn { 386 if p.ProdVertex == nil { 387 if evt != nil { 388 evt.Particles[p.Barcode] = p 389 } 390 if origEvt != nil && origEvt != evt { 391 origEvt.removeParticle(p.Barcode) 392 } 393 } 394 } 395 396 for _, p := range vtx.ParticlesOut { 397 if evt != nil { 398 evt.Particles[p.Barcode] = p 399 } 400 if origEvt != nil && origEvt != evt { 401 origEvt.removeParticle(p.Barcode) 402 } 403 } 404 return err 405 } 406 407 // AddParticleIn adds a particle to the list of in-coming particles to this vertex 408 func (vtx *Vertex) AddParticleIn(p *Particle) error { 409 var err error 410 if p == nil { 411 return errNilParticle 412 } 413 // if p had a decay vertex, remove it from that vertex's list 414 if p.EndVertex != nil { 415 err = p.EndVertex.removeParticleIn(p) 416 if err != nil { 417 return err 418 } 419 } 420 // make sure we don't add it twice... 421 err = vtx.removeParticleIn(p) 422 if err != nil { 423 return err 424 } 425 if p.Barcode == 0 { 426 p.Barcode = vtx.Event.pBarcode() 427 } 428 p.EndVertex = vtx 429 vtx.ParticlesIn = append(vtx.ParticlesIn, p) 430 vtx.Event.Particles[p.Barcode] = p 431 return err 432 } 433 434 // AddParticleOut adds a particle to the list of out-going particles to this vertex 435 func (vtx *Vertex) AddParticleOut(p *Particle) error { 436 var err error 437 if p == nil { 438 return errNilParticle 439 } 440 // if p had a production vertex, remove it from that vertex's list 441 if p.ProdVertex != nil { 442 err = p.ProdVertex.removeParticleOut(p) 443 if err != nil { 444 return err 445 } 446 } 447 // make sure we don't add it twice... 448 err = vtx.removeParticleOut(p) 449 if err != nil { 450 return err 451 } 452 if p.Barcode == 0 { 453 p.Barcode = vtx.Event.pBarcode() 454 } 455 p.ProdVertex = vtx 456 vtx.ParticlesOut = append(vtx.ParticlesOut, p) 457 vtx.Event.Particles[p.Barcode] = p 458 return err 459 } 460 461 func (vtx *Vertex) removeParticleIn(p *Particle) error { 462 var err error 463 nparts := len(vtx.ParticlesIn) 464 switch nparts { 465 case 0: 466 //FIXME: logical error ? 467 return err 468 } 469 idx := -1 470 for i, pp := range vtx.ParticlesIn { 471 if pp == p { 472 idx = i 473 break 474 } 475 } 476 if idx >= 0 { 477 copy(vtx.ParticlesIn[idx:], vtx.ParticlesIn[idx+1:]) 478 vtx.ParticlesIn[len(vtx.ParticlesIn)-1] = nil 479 vtx.ParticlesIn = vtx.ParticlesIn[:len(vtx.ParticlesIn)-1] 480 } 481 return err 482 } 483 484 func (vtx *Vertex) removeParticleOut(p *Particle) error { 485 var err error 486 nparts := len(vtx.ParticlesOut) 487 switch nparts { 488 case 0: 489 //FIXME: logical error ? 490 return err 491 } 492 idx := -1 493 for i, pp := range vtx.ParticlesOut { 494 if pp == p { 495 idx = i 496 break 497 } 498 } 499 if idx >= 0 { 500 copy(vtx.ParticlesOut[idx:], vtx.ParticlesOut[idx+1:]) 501 n := len(vtx.ParticlesOut) 502 vtx.ParticlesOut[n-1] = nil 503 vtx.ParticlesOut = vtx.ParticlesOut[:n-1] 504 } 505 return err 506 } 507 508 // Print prints the vertex to w in a human-readable format 509 func (vtx *Vertex) Print(w io.Writer) error { 510 var ( 511 err error 512 zero fmom.PxPyPzE 513 ) 514 if vtx.Barcode != 0 { 515 if vtx.Position != zero { 516 _, err = fmt.Fprintf( 517 w, 518 "Vertex:%9d ID:%5d (X,cT)=%+9.2e,%+9.2e,%+9.2e,%+9.2e\n", 519 vtx.Barcode, 520 vtx.ID, 521 vtx.Position.X(), 522 vtx.Position.Y(), 523 vtx.Position.Z(), 524 vtx.Position.T(), 525 ) 526 } else { 527 _, err = fmt.Fprintf( 528 w, 529 "GenVertex:%9d ID:%5d (X,cT):0\n", 530 vtx.Barcode, 531 vtx.ID, 532 ) 533 } 534 } else { 535 // if the vertex doesn't have a unique barcode assigned, then 536 // we print its memory address instead.. so that the 537 // print out gives us a unique tag for the particle. 538 if vtx.Position != zero { 539 _, err = fmt.Fprintf( 540 w, 541 "Vertex:%p ID:%5d (X,cT)=%+9.2e,%+9.2e,%+9.2e,%+9.2e\n", 542 vtx, 543 vtx.ID, 544 vtx.Position.X(), 545 vtx.Position.Y(), 546 vtx.Position.Z(), 547 vtx.Position.T(), 548 ) 549 550 } else { 551 _, err = fmt.Fprintf( 552 w, 553 "GenVertex:%9d ID:%5d (X,cT):0\n", 554 vtx.Barcode, 555 vtx.ID, 556 ) 557 } 558 } 559 if err != nil { 560 return err 561 } 562 563 // print the weights if any 564 if len(vtx.Weights.Slice) > 0 { 565 _, err = fmt.Fprintf(w, " Wgts(%d)=", len(vtx.Weights.Slice)) 566 if err != nil { 567 return err 568 } 569 for _, weight := range vtx.Weights.Slice { 570 _, err = fmt.Fprintf(w, "%e ", weight) 571 if err != nil { 572 return err 573 } 574 } 575 _, err = fmt.Fprintf(w, "\n") 576 if err != nil { 577 return err 578 } 579 } 580 // sort incoming particles by barcode 581 sort.Sort(Particles(vtx.ParticlesIn)) 582 583 // print out all incoming particles 584 for i, p := range vtx.ParticlesIn { 585 if i == 0 { 586 _, err = fmt.Fprintf(w, " I:%2d", len(vtx.ParticlesIn)) 587 } else { 588 _, err = fmt.Fprintf(w, " ") 589 } 590 if err != nil { 591 return err 592 } 593 err = p.dump(w) 594 if err != nil { 595 return err 596 } 597 _, err = fmt.Fprintf(w, "\n") 598 if err != nil { 599 return err 600 } 601 } 602 603 // sort outgoing particles by barcode 604 sort.Sort(Particles(vtx.ParticlesOut)) 605 606 // print out all outgoing particles 607 for i, p := range vtx.ParticlesOut { 608 if i == 0 { 609 _, err = fmt.Fprintf(w, " O:%2d", len(vtx.ParticlesOut)) 610 } else { 611 _, err = fmt.Fprintf(w, " ") 612 } 613 if err != nil { 614 return err 615 } 616 err = p.dump(w) 617 if err != nil { 618 return err 619 } 620 _, err = fmt.Fprintf(w, "\n") 621 if err != nil { 622 return err 623 } 624 } 625 return err 626 } 627 628 // HeavyIon holds additional information for heavy-ion collisions 629 type HeavyIon struct { 630 NCollHard int // number of hard scatterings 631 NPartProj int // number of projectile participants 632 NPartTarg int // number of target participants 633 NColl int // number of NN (nucleon-nucleon) collisions 634 NNwColl int // Number of N-Nwounded collisions 635 NwNColl int // Number of Nwounded-N collisons 636 NwNwColl int // Number of Nwounded-Nwounded collisions 637 SpectatorNeutrons int // Number of spectators neutrons 638 SpectatorProtons int // Number of spectators protons 639 ImpactParameter float32 // Impact Parameter(fm) of collision 640 EventPlaneAngle float32 // Azimuthal angle of event plane 641 Eccentricity float32 // eccentricity of participating nucleons in the transverse plane (as in phobos nucl-ex/0510031) 642 SigmaInelNN float32 // nucleon-nucleon inelastic (including diffractive) cross-section 643 } 644 645 // CrossSection is used to store the generated cross section. 646 // This type is meant to be used to pass, on an event by event basis, 647 // the current best guess of the total cross section. 648 // It is expected that the final cross section will be stored elsewhere. 649 type CrossSection struct { 650 Value float64 // value of the cross-section (in pb) 651 Error float64 // error on the value of the cross-section (in pb) 652 //IsSet bool 653 } 654 655 // PdfInfo holds informations about the partons distribution functions 656 type PdfInfo struct { 657 ID1 int // flavour code of first parton 658 ID2 int // flavour code of second parton 659 LHAPdf1 int // LHA PDF id of first parton 660 LHAPdf2 int // LHA PDF id of second parton 661 X1 float64 // fraction of beam momentum carried by first parton ("beam side") 662 X2 float64 // fraction of beam momentum carried by second parton ("target side") 663 ScalePDF float64 // Q-scale used in evaluation of PDF's (in GeV) 664 Pdf1 float64 // PDF (id1, x1, Q) 665 Pdf2 float64 // PDF (id2, x2, Q) 666 } 667 668 // Flow represents a particle's flow and keeps track of an arbitrary number of flow patterns within a graph (i.e. color flow, charge flow, lepton number flow,...) 669 // 670 // Flow patterns are coded with an integer, in the same manner as in Herwig. 671 // Note: 0 is NOT allowed as code index nor as flow code since it 672 // is used to indicate null. 673 // 674 // This class can be used to keep track of flow patterns within 675 // a graph. An example is color flow. If we have two quarks going through 676 // an s-channel gluon to form two more quarks: 677 // 678 // \q1 /q3 then we can keep track of the color flow with the 679 // \_______/ HepMC::Flow class as follows: 680 // / g \. 681 // /q2 \q4 682 // 683 // lets say the color flows from q2-->g-->q3 and q1-->g-->q4 684 // the individual colors are unimportant, but the flow pattern is. 685 // We can capture this flow by assigning the first pattern (q2-->g-->q3) 686 // a unique (arbitrary) flow code 678 and the second pattern (q1-->g-->q4) 687 // flow code 269 ( you can ask HepMC::Flow to choose 688 // a unique code for you using Flow::set_unique_icode() ). 689 // these codes with the particles as follows: 690 // 691 // q2->flow().set_icode(1,678); 692 // g->flow().set_icode(1,678); 693 // q3->flow().set_icode(1,678); 694 // q1->flow().set_icode(1,269); 695 // g->flow().set_icode(2,269); 696 // q4->flow().set_icode(1,269); 697 // 698 // later on if we wish to know the color partner of q1 we can ask for a list 699 // of all particles connected via this code to q1 which do have less than 700 // 2 color partners using: 701 // 702 // vector<GenParticle*> result=q1->dangling_connected_partners(q1->icode(1),1,2); 703 // 704 // this will return a list containing q1 and q4. 705 // 706 // vector<GenParticle*> result=q1->connected_partners(q1->icode(1),1,2); 707 // 708 // would return a list containing q1, g, and q4. 709 type Flow struct { 710 Particle *Particle // the particle this flow describes 711 Icode map[int]int // flow patterns as (code_index, icode) 712 } 713 714 // Polarization holds informations about a particle's polarization 715 type Polarization struct { 716 Theta float64 // polar angle of polarization in radians [0, math.Pi) 717 Phi float64 // azimuthal angle of polarization in radians [0, 2*math.Pi) 718 } 719 720 // Weights holds informations about the event's and vertices' generation weights. 721 type Weights struct { 722 Slice []float64 // the slice of weight values 723 Map map[string]int // the map of name->index-in-the-slice 724 } 725 726 // Add adds a new weight with name n and value v. 727 func (w *Weights) Add(n string, v float64) error { 728 _, ok := w.Map[n] 729 if ok { 730 return fmt.Errorf("hepmc.Weights.Add: name [%s] already in container", n) 731 } 732 idx := len(w.Slice) 733 w.Map[n] = idx 734 w.Slice = append(w.Slice, v) 735 return nil 736 } 737 738 // At returns the weight's value named n. 739 func (w Weights) At(n string) float64 { 740 idx, ok := w.Map[n] 741 if ok { 742 return w.Slice[idx] 743 } 744 panic("hepmc.Weights.At: invalid name [" + n + "]") 745 } 746 747 // NewWeights creates a new set of weights. 748 func NewWeights() Weights { 749 return Weights{ 750 Slice: make([]float64, 0, 1), 751 Map: make(map[string]int), 752 } 753 } 754 755 // EOF