Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/hepmc2root: consider implement this conversion tool #959

Closed
sbinet opened this issue Sep 14, 2022 · 1 comment · Fixed by #962
Closed

cmd/hepmc2root: consider implement this conversion tool #959

sbinet opened this issue Sep 14, 2022 · 1 comment · Fixed by #962

Comments

@sbinet
Copy link
Member

sbinet commented Sep 14, 2022

as noted in:

converting HepMC2 files to ROOT should be relatively easy. (and useful)

@sbinet
Copy link
Member Author

sbinet commented Sep 14, 2022

#!/usr/bin/env python
# -----------------------------------------------------------------------
# File: hepmc2root.py
# Description: write events in HepMC2 format to a flat ROOT ntuple using
#              variable length arrays
#
#
#     status = +- (10 * i + j)
#     + : still remaining particles
#     - : decayed/branched/fragmented/... and not remaining
#     i = 1 - 9 : stage of event generation inside PYTHIA
#     i = 10 -19 : reserved for future expansion
#     i >= 20 : free for add-on programs
#     j = 1 - 9 : further specification
#
# In detail, the list of used or foreseen status codes is:
#
#     11 - 19 : beam particles
#         11 : the event as a whole
#         12 : incoming beam
#         13 : incoming beam-inside-beam (e.g. gamma inside e)
#         14 : outgoing elastically scattered
#         15 : outgoing diffractively scattered
#     21 - 29 : particles of the hardest subprocess
#         21 : incoming
#         22 : intermediate (intended to have preserved mass)
#         23 : outgoing
#         24 : outgoing, nonperturbatively kicked out in diffraction
#
# Created: fall   2017 Harrison B. Prosper
# Updated: 04-Dec-2017 HBP add creation vertex (x,y,z) of particles.
#          15-Apr-2019 HBP test that ROOT can be imported
#          31-Jan-2020 HBP make compatible with Python 3
# -----------------------------------------------------------------------
import os, sys
try:
    import ROOT
except:
    sys.exit('\n\t*** This program uses ROOT! ***\n')
from math import sqrt
from time import ctime
from pnames import particleName
# -----------------------------------------------------------------------
def nameonly(s):
    import posixpath
    return posixpath.splitext(posixpath.split(s)[1])[0]

TREENAME= "Events"
MAXPART = 5000
debug = 0

class hepmc2root:
    
    def __init__(self, filename, outfilename=None, treename=TREENAME, complevel=2):

        # check that file exists
        
        if not os.path.exists(filename):
            sys.exit("** hepmc2root.py: can't open file %s" % \
                         filename)
        self.inp = open(filename)
        inp = self.inp

        # get version number of HepMC
        
        self.header = [] # cache HepMC header
        version = None
        for line in inp:
            self.header.append(line)
            version = str.strip(line)
            if version == '': continue
            token = str.split(version)
            if token[0] == 'HepMC::Version':
                version = token[1]
            break
        else:
            sys.exit("** hepmc2root.py: format problem in file %s" % \
                         filename)
            
        print("HepMC version: %s" % version)

        # skip start of listing
        
        for line in inp:
            self.header.append(line)
            break

        # open output root file

        if outfilename == None:
            outfilename = '%s.root' % nameonly(filename)
            
        self.file = ROOT.TFile(outfilename, "recreate")
        self.tree = ROOT.TTree(treename, 'created: %s HepMC %s' % \
                                   (ctime(), version))

        # define event struct
        
        self.struct = '''struct Bag {
        int    Event_number;
        int    Event_numberMP;
        double Event_scale;
        double Event_alphaQCD;
        double Event_alphaQED;
        int    Event_barcodeSPV;
        int    Event_numberV;
        int    Event_barcodeBP1;
        int    Event_barcodeBP2;
        int    Event_numberP;

        double Xsection_value;
        double Xsection_error;

        int    PDF_parton1;
        int    PDF_parton2;
        double PDF_x1;
        double PDF_x2;
        double PDF_Q2;
        double PDF_x1f;
        double PDF_x2f;
        int    PDF_id1;
        int    PDF_id2;

        double Particle_x[%(size)d];
        double Particle_y[%(size)d];
        double Particle_z[%(size)d];
        double Particle_ctau[%(size)d];

        double Particle_barcode[%(size)d];
        int    Particle_pid[%(size)d];
        double Particle_px[%(size)d];
        double Particle_py[%(size)d];
        double Particle_pz[%(size)d];
        double Particle_energy[%(size)d];
        double Particle_mass[%(size)d];
        int    Particle_status[%(size)d];
        int    Particle_d1[%(size)d];
        int    Particle_d2[%(size)d];
};''' % {'size': MAXPART}

        # indices to vertices
        
        self.pvertex = [0]*MAXPART
        
        # create struct
        
        ROOT.gROOT.ProcessLine(self.struct)
        from ROOT import Bag
        self.bag = Bag()

        # create branches
        
        self.branch = []
        recs = str.split(self.struct, '\n')[1:-1]
        for rec in recs:
            t = str.split(rec)
            if len(t) == 0: continue
                
            fmt, name = t
            T = str.upper(fmt[0])
            name = name[:-1] # skip ";"
            # check for variable length array
            if name[-1] == ']':
                field = str.split(name, '[')[0]
                fmt   = '%s[Event_numberP]/%s' % (field, T)
            else:
                field = name
                fmt   = '%s/%s' % (field, T)
            self.branch.append(self.tree.Branch(field,
                                                ROOT.addressof(self.bag, field),
                                                    fmt))
        # list branches
        
        for ii, b in enumerate(self.branch):
            bname = b.GetName()
            leaves= b.GetListOfLeaves()
            if leaves == None:
                sys.exit("** hepmc2root: no list of leaves found for branch %s" % bname)
            leaf = leaves[0]
            if leaf == None:
                sys.exit("** hepmc2root: no leaf found for branch %s" % bname)
            leafname = leaf.GetName()
            leaftype = leaf.GetTypeName()
            print("%4d\t%-20s\t%s" % (ii+1, bname, leaftype))
            
    def __del__(self):
        self.tree.Write("", ROOT.TObject.kOverwrite)
        
    def __str__(self, index):
        bag = self.bag
        d   = " <%4d, %4d>" % (bag.Particle_d1[index], bag.Particle_d2[index])
        px  = bag.Particle_px[index]
        py  = bag.Particle_py[index]
        pt  = sqrt(px**2+py**2)
        rec = '%-14s %7d %4d %3d %7.1f (%7.1f, %7.1f, %7.1f, %7.1f)%s' \
          % (particleName(bag.Particle_pid[index]),
             bag.Particle_pid[index],
             bag.Particle_barcode[index],
             bag.Particle_status[index],
             pt, 
             bag.Particle_energy[index],
             bag.Particle_px[index],
             bag.Particle_py[index],
             bag.Particle_pz[index],
             d)
        return rec

    def __call__(self):
        inp = self.inp
        bag = self.bag

        self.event = [] # cache HepMC event in original format
        
        # find start of event
        
        token = None
        for line in inp:
            self.event.append(line)
            token = str.split(line)
            key   = token[0]
            if key != 'E': continue
            if debug > 0:
                print('BEGIN event')
            break
        else:
            return False

        if token == None:
            sys.exit("** hepmc2root.py: can't find start of event")

        bag.Event_number     = int(token[1])
        bag.Event_numberMP   = int(token[2])  # number of multi-particle interactions
        bag.Event_scale      = float(token[3])
        bag.Event_alphaQCD   = float(token[4])
        bag.Event_alphaQED   = float(token[5])
        bag.Event_processID  = int(token[6])
        bag.Event_barcodeSPV = int(token[7])
        bag.Event_numberV    = int(token[8])  # number of vertices in event
        bag.Event_barcodeBP1 = int(token[9])  # barcode beam particle 1 
        bag.Event_barcodeBP2 = int(token[10]) # barcode beam particle 2
        bag.Event_numberP    = 0               # number of particles
        
        if debug > 0:
            print("\tbarcode 1: %d" % self.barcode1)
            print("\tbarcode 2: %d" % self.barcode2)

        self.vertex = {}

        for line in inp:
            self.event.append(line)
            token = str.split(line)
            key = token[0]

            if key == 'C':
                # CROSS SECTION
                bag.Xsection_value = float(token[1])
                bag.Xsection_error = float(token[2])
                if debug > 0:
                    print("\tcross section: %10.3e +\- %10.3e pb" % \
                      (bag.Xsection_value, bag.Xsection_error))
                      
            elif key == 'F':
                # PDF INFO
                bag.PDF_parton1  = int(token[1])
                bag.PDF_parton2  = int(token[2])
                bag.PDF_x1       = float(token[3])
                bag.PDF_x2       = float(token[4])
                bag.PDF_Q2       = float(token[5])
                bag.PDF_x1f      = float(token[6])
                bag.PDF_x2f      = float(token[7])
                bag.PDF_id1      = int(token[8])
                bag.PDF_id2      = int(token[9])

                if debug > 0:
                    print('\tfound PDF info')

            elif key == 'V':
                # VERTEX
                vbarcode = int(token[1])
                self.vertex[vbarcode] = [-1, -1]
                x    = float(token[3])
                y    = float(token[4])
                z    = float(token[5])
                ctau = float(token[6])
                nout = int(token[8])
                if debug > 0:
                    if debug > 1:
                        print("\t%s" % token)
                    print('\tvertex(barcode): %10d' % vbarcode)
                    print('\tvertex(count):   %10d' % nout)

                # particles pertaining to this vertex follow immediately
                # after the vertex
                for ii in range(nout):
                    for line in inp:
                        self.event.append(line)
                        token  = str.split(line)
                        if debug > 1:
                            print("\t%s" % token)
                        key    = token[0]
                        if key != 'P':
                            sys.exit("** hepmc2root: faulty event record\n" + line)

                        if bag.Event_numberP < MAXPART:
                            index = bag.Event_numberP
                            bag.Event_numberP += 1

                            bag.Particle_x[index]       = x
                            bag.Particle_y[index]       = y
                            bag.Particle_z[index]       = z
                            bag.Particle_ctau[index]    = ctau
                            
                            bag.Particle_barcode[index] = int(token[1])
                            bag.Particle_pid[index]     = int(token[2])
                            bag.Particle_px[index]      = float(token[3])
                            bag.Particle_py[index]      = float(token[4])
                            bag.Particle_pz[index]      = float(token[5])
                            bag.Particle_energy[index]  = float(token[6])
                            bag.Particle_mass[index]    = float(token[7])
                            bag.Particle_status[index]  = int(token[8])
                            self.pvertex[index]         = int(token[11])

                            if ii == 0:
                                self.vertex[vbarcode][0] = index
                            else:
                                self.vertex[vbarcode][1] = index
                        
                        break
                    else:
                        return False
                    
            if len(self.vertex) >= bag.Event_numberV:
                for index in range(bag.Event_numberP):
                    code = self.pvertex[index]
                    if code in self.vertex:
                        d = self.vertex[code]
                        bag.Particle_d1[index] = d[0]
                        bag.Particle_d2[index] = d[1]
                    else:
                        bag.Particle_d1[index] = -1
                        bag.Particle_d2[index] = -1

                # fill ntuple
                
                self.file.cd()
                self.tree.Fill()
                
                return True
        else:
            return False
        
    def printTable(self):
        for ii in xrange(self.bag.Event_numberP):
            print("%4d\t%s" % (ii, self.__str__(ii)))
# -----------------------------------------------------------------------    
def main():
    argv = sys.argv[1:]
    argc = len(argv)
    if argc < 1:
        sys.exit('''
    Usage:
        ./hepmc2root.py <HepMC-file> [output root file = <name>.root]
        ''')

    filename = argv[0]
    if argc > 1:
        outfilename = argv[1]
    else:
        outfilename = '%s.root' % nameonly(filename)

    stream = hepmc2root(filename, outfilename)

    ii = 0
    while stream():
        if ii % 1000 == 0:
            print(ii)
        ii += 1
# -----------------------------------------------------------------------
try:
    main()
except KeyboardInterrupt:
    print('\nciao!')

sbinet added a commit to sbinet-hep/hep that referenced this issue Sep 23, 2022
Fixes go-hep#959.

Signed-off-by: Sebastien Binet <binet@cern.ch>
sbinet added a commit to sbinet-hep/hep that referenced this issue Sep 23, 2022
Fixes go-hep#959.

Signed-off-by: Sebastien Binet <binet@cern.ch>
sbinet added a commit to sbinet-hep/hep that referenced this issue Oct 3, 2022
Fixes go-hep#959.

Signed-off-by: Sebastien Binet <binet@cern.ch>
@sbinet sbinet mentioned this issue Oct 3, 2022
@sbinet sbinet closed this as completed in 100af9d Oct 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant