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