Пример #1
0
    def make_particles_from_block(self, block):
        ''' Take a block and use simple rules to construct particles 
        '''
        #take a block and find its parents (clusters and tracks)
        parents = block.element_uniqueids

        if (len(parents) == 1) & (IdCoder.is_ecal(parents[0])):
            #print "make photon"
            self.make_photon(parents)

        elif ((len(parents) == 2) & (block.count_ecal() == 1) &
              (block.count_tracks() == 1)):
            #print "make hadron"
            self.make_hadron(parents)

        elif ((len(parents) == 3) & (block.count_ecal() == 1) &
              (block.count_tracks() == 1) & (block.count_hcal() == 1)):
            #print "make hadron and photon"
            #probably not right but illustrates splitting of parents for more than one particle
            hparents = [
            ]  # will contain parents for the Hadron which gets everything except the
            #hcal which is used for the photom
            for elem in parents:
                if (IdCoder.is_hcal(elem)):
                    self.make_photon({elem})
                else:
                    hparents.append(elem)
            self.make_hadron(hparents)

        else:
            pass
Пример #2
0
    def short_elements_string(self):
        ''' Construct a string description of each of the elements in a block.

        The elements are given a short name E/H/T according to ecal/hcal/track
        and then sequential numbering starting from 0, this naming is also used to index the
        matrix of distances. The full unique id is also given.
        For example:-
        elements: {
        E0:1104134446736:SmearedCluster : ecal_in       0.57  0.33 -2.78
        H1:2203643940048:SmearedCluster : hcal_in       6.78  0.35 -2.86
        T2:3303155568016:SmearedTrack   :    5.23    4.92  0.34 -2.63
        }
        '''

        count = 0
        elemdetails = "    elements:\n"
        for uid in self.element_uniqueids:
            elemdetails += "{shortname:>7}{count} = {strdescrip:9} value={val:5.1f} ({uid})\n".format(
                shortname=IdCoder.type_letter(uid),
                count=count,
                strdescrip=IdCoder.pretty(uid),
                val=IdCoder.get_value(uid),
                uid=uid)
            count = count + 1
        return elemdetails
Пример #3
0
    def _edge_type(self):
        ''' produces an edge_type string eg "ecal_track"
            the order of id1 an id2 does not matter, 
            eg for one track and one ecal the type will always be "ecal_track" (and never be a "track_ecal")         
        '''
        #consider creating an ENUM instead for the edge_type
        shortid1 = IdCoder.type_letter(self.id1)
        shortid2 = IdCoder.type_letter(self.id2)
        if shortid1 == shortid2:
            if shortid1 == "h":
                return "hcal_hcal"
            elif shortid1 == "e":
                return "ecal_ecal"
            elif shortid1 == "t":
                return "track_track"
        elif (shortid1 == "h" and shortid2 == "t"
              or shortid1 == "t" and shortid2 == "h"):
            return "hcal_track"
        elif (shortid1 == "e" and shortid2 == "t"
              or shortid1 == "t" and shortid2 == "e"):
            return "ecal_track"
        elif (shortid1 == "e" and shortid2 == "h"
              or shortid1 == "h" and shortid2 == "e"):
            return "ecal_hcal"

        return "unknown"
Пример #4
0
    def short_elements_string(self):
        ''' Construct a string description of each of the elements in a block.

        The elements are given a short name E/H/T according to ecal/hcal/track
        and then sequential numbering starting from 0, this naming is also used to index the
        matrix of distances. The full unique id is also given.
        For example:-
        elements: {
        E0:1104134446736:SmearedCluster : ecal_in       0.57  0.33 -2.78
        H1:2203643940048:SmearedCluster : hcal_in       6.78  0.35 -2.86
        T2:3303155568016:SmearedTrack   :    5.23    4.92  0.34 -2.63
        }
        '''

        count = 0
        elemdetails = "    elements:\n"
        for uid in self.element_uniqueids:
            elemdetails += "{shortname:>7}{count} = {strdescrip:9} value={val:5.1f} ({uid})\n".format(
                shortname=IdCoder.type_letter(uid),
                count=count,
                strdescrip=IdCoder.pretty(uid),
                val=IdCoder.get_value(uid),
                uid=uid)
            count = count + 1
        return elemdetails
Пример #5
0
 def make_particles_from_block(self, block):
     ''' Take a block and use simple rules to construct particles 
     '''
     #take a block and find its parents (clusters and tracks)
     parents = block.element_uniqueids
     
     if  (len(parents) == 1) & (IdCoder.is_ecal(parents[0])):
         #print "make photon"
         self.make_photon(parents)
         
     elif ( (len(parents) == 2)  & (block.count_ecal() == 1 ) & (block.count_tracks() == 1)):
         #print "make hadron" 
         self.make_hadron(parents)
         
     elif  ((len(parents) == 3)  & (block.count_ecal() == 1) & (block.count_tracks() == 1) & (block.count_hcal() == 1)):
             #print "make hadron and photon"
             #probably not right but illustrates splitting of parents for more than one particle
             hparents = [] # will contain parents for the Hadron which gets everything except the 
                           #hcal which is used for the photom
             for elem in parents:
                 if (IdCoder.is_hcal(elem)):
                     self.make_photon({elem})
                 else :
                     hparents.append(elem)    
             self.make_hadron(hparents)
 
     else :
         pass
Пример #6
0
    def test_papasevent(self):
        
        #create a dummy papasevent
        papasevent = PapasEvent(0)
        ecals = dict()
        tracks = dict()
        mixed = dict()
        
        for i in range(0, 2):
            uid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.ECALCLUSTER, i, 't', 4.5)
            ecals[uid] = uid
            papasevent.history[uid] = Node(uid)
            uidt = IdCoder.make_id(IdCoder.PFOBJECTTYPE.TRACK, i, 's', 4.5)
            tracks[uidt] = uidt  
            papasevent.history[uidt] = Node(uidt)
            papasevent.history[uidt].add_child(papasevent.history[uid])

        lastid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.ECALCLUSTER, 3, 't', 3)
        ecals[lastid] = lastid   
        papasevent.history[lastid] = Node(lastid)
        papasevent.add_collection(ecals)
        papasevent.add_collection(tracks)
        
        #create HistoryHelper
        hhelper =  HistoryHelper(papasevent)
        
        #get all ids in event
        ids =  hhelper.event_ids()
        self.assertTrue(len(ids) == 5)

        #check id_from_pretty
        self.assertTrue(hhelper.id_from_pretty('et3') == lastid)
        
        #check get_linked_ids
        
        linked = hhelper.get_linked_ids(lastid) #everything linked to lastid (which is just lastid)
        self.assertTrue(linked[0] == lastid and len(linked) == 1)        
        self.assertTrue( hhelper.get_linked_ids( ids[0], direction="undirected")[1] == hhelper.id_from_pretty('ts0'))
        self.assertTrue( hhelper.get_linked_ids( ids[0], direction="parents")== hhelper.get_linked_ids( ids[0], direction="undirected"))
        self.assertTrue( hhelper.get_linked_ids(ids[0], direction="children") == [hhelper.id_from_pretty('et0')])
        
        #filter_ids
        self.assertTrue( len(hhelper.filter_ids(ids, 'ts')) == 2)
        self.assertTrue( hhelper.filter_ids(ids, 'no') == [])
        
        #get_collection
        self.assertTrue( len( hhelper.get_collection(ids[1:2], 'no'))  == 0)
        self.assertTrue( len( hhelper.get_collection([99], 'no'))  == 0)
        self.assertTrue( len(hhelper.get_collection(ids[0:2], 'ts')) == 1)
        pass
    
        #get_history_subgroups
        subgroups = hhelper.get_history_subgroups()
        self.assertTrue(len(subgroups) == 3)
        
        #get_linked_collection
        self.assertTrue(hhelper.get_linked_collection( hhelper.id_from_pretty('et0'), 'ts').keys() == [hhelper.id_from_pretty('ts0')])
        self.assertRaises(KeyError, hhelper.get_linked_collection, 0, 'ts')
        self.assertTrue(len(hhelper.get_linked_collection( hhelper.id_from_pretty('et0'), 'no')) == 0)
Пример #7
0
    def process(self, event):
        event.papasevent = PapasEvent(event.iEv)
        papasevent = event.papasevent

        #make a dict from the gen_particles list so that it can be stored into the papasevent collections
        gen_particles = getattr(event, self.cfg_ana.gen_particles)
        gen_particles_collection = {}
        for g in gen_particles:
            #set the papas identifiers for use in DAG
            g.set_dagid(
                IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE,
                                g.objid()[0], 'g',
                                g.p4().E()))
            gen_particles_collection[g.dagid()] = g

        #make a dict from the rec_particles list so that it can be stored into the papasevent collections
        rec_particles = getattr(event, self.cfg_ana.rec_particles)

        #if there are no rec_particles we assume this was an evernt discarded during reconstruction and skip it
        if len(rec_particles) == 0:
            self.mainLogger.error(
                'no reconsrtucted particles found -> Event discarded')
            return False
        rec_particles_collection = {}
        for r in rec_particles:
            #set the papas identifiers for use in DAG
            r.set_dagid(
                IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE,
                                r.objid()[0], 'r',
                                r.p4().E()))
            rec_particles_collection[r.dagid()] = r

        #create the history links for relationship between gen and rec particles
        particle_links = getattr(event, self.cfg_ana.gen_rec_links)
        for plink in particle_links:
            genid = None
            recid = None
            for g in gen_particles:
                if g.objid() == plink.id1():
                    genid = g.dagid()
                    break
            for g in rec_particles:
                if g.objid() == plink.id2():
                    recid = g.dagid()
                    break
            if recid == None or genid == None:
                self.mainLogger.error(
                    'Error: One of the particles in the Particle Link was not found-> discarding event'
                )
                return False
            child = papasevent.history.setdefault(
                recid,
                Node(recid))  #creates a new node if it is not there already
            parent = papasevent.history.setdefault(genid, Node(genid))
            parent.add_child(child)

        papasevent.add_collection(gen_particles_collection)
        papasevent.add_collection(rec_particles_collection)
Пример #8
0
 def _graph_add_topological_block(self, graph, graphnodes, pfblock):
     '''this adds the block links (distance, is_linked) onto the DAG in red'''
     for edge in pfblock.edges.itervalues():
         if  edge.linked: 
             label = "{:.1E}".format(edge.distance)
             if edge.distance == 0:
                 label = "0"
             graph.add_edge(pydot.Edge(graphnodes[IdCoder.pretty(edge.id1)],
                                       graphnodes[IdCoder.pretty(edge.id2)],
                                       label=label, style="dashed", color="red", arrowhead="none", arrowtail="none", fontsize='7'))
Пример #9
0
 def __init__(self, uid, layer):
     ''' uid is unique integer from 101-199 for ecal cluster
           unique integer from 201-299 for hcal cluster
           layer is ecal/hcal
     '''
     if (layer == 'ecal_in'):
         self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.ECALCLUSTER, uid, 't')
     elif (layer == 'hcal_in'):
         self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.HCALCLUSTER, uid,  't')
     else:
         assert false
     self.layer = layer
     self.uid = uid
     self.energy=0
Пример #10
0
 def __init__(self, uid, layer):
     ''' uid is unique integer from 101-199 for ecal cluster
           unique integer from 201-299 for hcal cluster
           layer is ecal/hcal
     '''
     if (layer == 'ecal_in'):
         self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.ECALCLUSTER,
                                         uid, 't')
     elif (layer == 'hcal_in'):
         self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.HCALCLUSTER,
                                         uid, 't')
     else:
         assert false
     self.layer = layer
     self.uid = uid
     self.energy = 0
Пример #11
0
 def simplify_blocks(self, block, history_nodes=None):
     ''' Block: a block which contains list of element ids and set of edges that connect them
         history_nodes: optional dictionary of Nodes with element identifiers in each node
     
     returns a dictionary of new split blocks
         
     The goal is to remove, if needed, some links from the block so that each track links to 
     at most one hcal within a block. In some cases this may separate a block into smaller
     blocks (splitblocks). The BlockSplitter is used to return the new smaller blocks.
      If history_nodes are provided then the history will be updated. Split blocks will 
      have the tracks and cluster elements as parents, and also the original block as a parent
     '''
     ids = block.element_uniqueids
     #create a copy of the edges and unlink some of these edges if needed
     newedges = copy.deepcopy(block.edges)
     if len(ids) > 1 :   
         for uid in ids :
             if IdCoder.is_track(uid):
                 # for tracks unlink all hcals except the closest hcal
                 linked_ids = block.linked_ids(uid, "hcal_track") # NB already sorted from small to large distance
                 if linked_ids != None and len(linked_ids) > 1:
                     first_hcal = True
                     for id2 in linked_ids:
                         newedge = newedges[Edge.make_key(uid, id2)]
                         if first_hcal:
                             first_dist = newedge.distance
                             first_hcal = False
                         else:
                             if newedge.distance == first_dist:
                                 pass 
                             newedge.linked = False 
     #create new block(s)               
     splitblocks = BlockSplitter(block.uniqueid, ids, newedges, len(self.splitblocks), 's', history_nodes).blocks
     return splitblocks
Пример #12
0
 def _graph_add_topological_block(self, graph, graphnodes, pfblock):
     '''this adds the block links (distance, is_linked) onto the DAG in red'''
     for edge in pfblock.edges.itervalues():
         if edge.linked:
             label = "{:.1E}".format(edge.distance)
             if edge.distance == 0:
                 label = "0"
             graph.add_edge(
                 pydot.Edge(graphnodes[IdCoder.pretty(edge.id1)],
                            graphnodes[IdCoder.pretty(edge.id2)],
                            label=label,
                            style="dashed",
                            color="red",
                            arrowhead="none",
                            arrowtail="none",
                            fontsize='7'))
Пример #13
0
 def __init__(self, uid):
     ''' uid is unique integer from 1-99
     '''
     self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.TRACK, uid, 't')
     self.uid = uid
     self.layer = 'tracker'
     self.energy = 0
Пример #14
0
 def __init__(self, uid):
     ''' uid is unique integer from 1-99
     '''
     self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.TRACK,  uid, 't')
     self.uid = uid
     self.layer = 'tracker'
     self.energy=0
Пример #15
0
 def __init__(self, uid,pdgid):
     ''' uid is unique integer from 601-699
         pdgid is particle uid eg 22 for photon
     '''
     self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE,  uid,'r')
     self.pdgid = pdgid
     self.uid = uid
Пример #16
0
    def edge_matrix_string(self):
        ''' produces a string containing the the lower part of the matrix of distances between elements
        elements are ordered as ECAL(E), HCAL(H), Track(T)
        for example:-

        distances:
                  E0       H1       T2       T3
         E0       .
         H1  0.0267        .
         T2  0.0000   0.0000        .
         T3  0.0287   0.0825      ---        .
         '''

        # make the header line for the matrix
        count = 0
        matrixstr = ""
        if len(self.element_uniqueids) > 1:
            matrixstr = "    distances:\n        "
            for e1 in self.element_uniqueids:
                # will produce short id of form E2 H3, T4 etc in tidy format
                elemstr = IdCoder.type_letter(e1) + str(count)
                matrixstr += "{:>8}".format(elemstr)
                count += 1
            matrixstr += "\n"

            #for each element find distances to all other items that are in the lower part of the matrix
            countrow = 0
            for e1 in self.element_uniqueids:  # this will be the rows
                countcol = 0
                rowstr = ""
                #make short name for the row element eg E3, H5 etc
                rowname = IdCoder.type_letter(e1) + str(countrow)
                for e2 in self.element_uniqueids:  # these will be the columns
                    countcol += 1
                    if e1 == e2:
                        rowstr += "       ."
                        break
                    elif self.get_edge(e1, e2).distance is None:
                        rowstr += "     ---"
                    elif not self.get_edge(e1, e2).linked:
                        rowstr += "     ---"
                    else:
                        rowstr += "{:8.4f}".format(
                            self.get_edge(e1, e2).distance)
                matrixstr += "{:>8}".format(rowname) + rowstr + "\n"
                countrow += 1
        return matrixstr
Пример #17
0
    def process(self, event):
        event.papasevent = PapasEvent(event.iEv)
        papasevent = event.papasevent

        #make a dict from the gen_particles list so that it can be stored into the papasevent collections
        gen_particles = getattr(event, self.cfg_ana.gen_particles)
        gen_particles_collection = {}
        for g in gen_particles:
            #set the papas identifiers for use in DAG
            g.set_dagid(IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, g.objid()[0], 'g', g.p4().E()))
            gen_particles_collection[g.dagid()] = g

        #make a dict from the rec_particles list so that it can be stored into the papasevent collections
        rec_particles = getattr(event, self.cfg_ana.rec_particles)

        #if there are no rec_particles we assume this was an evernt discarded during reconstruction and skip it
        if len(rec_particles) == 0:
            self.mainLogger.error('no reconsrtucted particles found -> Event discarded')
            return False
        rec_particles_collection = {}
        for r in rec_particles:
            #set the papas identifiers for use in DAG
            r.set_dagid(IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, r.objid()[0], 'r', r.p4().E()))
            rec_particles_collection[r.dagid()] = r

        #create the history links for relationship between gen and rec particles
        particle_links = getattr(event, self.cfg_ana.gen_rec_links)
        for plink in particle_links:
            genid = None
            recid = None
            for g in gen_particles:
                if g.objid() == plink.id1() :
                    genid = g.dagid()
                    break
            for g in rec_particles:
                if g.objid() == plink.id2() :
                    recid = g.dagid()
                    break            
            if recid==None or genid ==None:
                self.mainLogger.error('Error: One of the particles in the Particle Link was not found-> discarding event')
                return False 
            child = papasevent.history.setdefault(recid, Node(recid)) #creates a new node if it is not there already
            parent = papasevent.history.setdefault(genid, Node(genid))
            parent.add_child(child)

        papasevent.add_collection(gen_particles_collection)
        papasevent.add_collection(rec_particles_collection)
Пример #18
0
 def filter_ids(self, ids, type_and_subtype):
     ''' returns a filtered subset of ids which have a type_and_subtype that matchs the type_and_subtype argument
         eg merged_ecal_ids = filter_ids(ids, 'em')
         @param ids: a list of ids
         @param type_and_subtype: a two letter type and subtype eg 'es' for smeared ecal
     '''
     return [uid for uid in ids
             if IdCoder.type_and_subtype(uid) == type_and_subtype]
Пример #19
0
 def __init__(self, uid, pdgid):
     ''' uid is unique integer from 301-399
         pdgid is particle uid eg 22 for photon
     '''
     self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, uid, 'g')
     #print "particle: ",self.uniqueid," ",uid
     self.pdgid = pdgid
     self.uid = uid
Пример #20
0
 def __init__(self, uid, pdgid):
     ''' uid is unique integer from 601-699
         pdgid is particle uid eg 22 for photon
     '''
     self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, uid,
                                     'r')
     self.pdgid = pdgid
     self.uid = uid
Пример #21
0
 def color(self, node):
     '''used to color dag nodes
     @param node: a node in the DAG history'''
     cols = [
         "red", "lightblue", "green", "yellow", "cyan", "grey", "white",
         "pink"
     ]
     return cols[IdCoder.get_type(node.get_value())]
Пример #22
0
    def edge_matrix_string(self):
        ''' produces a string containing the the lower part of the matrix of distances between elements
        elements are ordered as ECAL(E), HCAL(H), Track(T)
        for example:-

        distances:
                  E0       H1       T2       T3
         E0       .
         H1  0.0267        .
         T2  0.0000   0.0000        .
         T3  0.0287   0.0825      ---        .
         '''

        # make the header line for the matrix
        count = 0
        matrixstr = ""
        if len(self.element_uniqueids) > 1:
            matrixstr = "    distances:\n        "
            for e1 in self.element_uniqueids :
                # will produce short id of form E2 H3, T4 etc in tidy format
                elemstr = IdCoder.type_letter(e1) + str(count)
                matrixstr += "{:>8}".format(elemstr)
                count += 1
            matrixstr += "\n"

            #for each element find distances to all other items that are in the lower part of the matrix
            countrow = 0
            for e1 in self.element_uniqueids : # this will be the rows
                countcol = 0
                rowstr = ""
                #make short name for the row element eg E3, H5 etc
                rowname = IdCoder.type_letter(e1) +str(countrow)
                for e2 in self.element_uniqueids:  # these will be the columns
                    countcol += 1
                    if e1 == e2:
                        rowstr += "       ."
                        break
                    elif self.get_edge(e1, e2).distance is None:
                        rowstr += "     ---"
                    elif not self.get_edge(e1, e2).linked:
                        rowstr += "     ---"
                    else :
                        rowstr += "{:8.4f}".format(self.get_edge(e1, e2).distance)
                matrixstr += "{:>8}".format(rowname) + rowstr + "\n"
                countrow += 1
        return matrixstr
Пример #23
0
 def reconstruct_muons(self, block):
     '''Reconstruct muons in block.'''
     uids = block.element_uniqueids
     for uid in uids:
         if IdCoder.is_track(uid) and \
            self.is_from_particle(uid, 'ps', 13):
             parent_ids = [block.uniqueid, uid]
             self.reconstruct_track(self.papasevent.get_object(uid),
                                    13, parent_ids)
Пример #24
0
 def __init__(self, uid, pdgid):
     ''' uid is unique integer from 301-399
         pdgid is particle uid eg 22 for photon
     '''
     self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, uid,
                                     'g')
     #print "particle: ",self.uniqueid," ",uid
     self.pdgid = pdgid
     self.uid = uid
Пример #25
0
 def id_from_pretty(self, pretty):
     ''' Searches to find the true id given a pretty id string
         Not super efficient but OK for occasional use
         eg uid = self.id_from_pretty('et103')
         @param: pretty is the easily readable name from the Identifier class which is shown in prints and plots eg 'et103'
     '''
     for uid in self.history.keys():
         if IdCoder.pretty(uid) == pretty:
             return uid
     return None
Пример #26
0
 def reconstruct_electrons(self, block):
     '''Reconstruct electrons in block.'''
     uids = block.element_uniqueids
     for uid in uids:
         if IdCoder.is_track(uid) and \
            self.is_from_particle(uid, 'ps', 11):
             parent_ids = [block.uniqueid, uid]
             track = self.papasevent.get_object(uid)
             ptc = self.reconstruct_track(track,
                                          11, parent_ids)
Пример #27
0
 def info(self):
     subclusterstr = str('sub(')
     for s in self.subclusters:
         subclusterstr += str('{:}, '.format(IdCoder.pretty(s.uniqueid)))
     subclusterstr += ")"
     return '{energy:7.2f} {theta:5.2f} {phi:5.2f} {sub}'.format(
         energy=self.energy,
         theta=math.pi / 2. - self.position.Theta(),
         phi=self.position.Phi(),
         sub=subclusterstr)
Пример #28
0
 def __init__(self, pfobjecttype, index, subtype='u', identifiervalue = 0.0):
     '''@param pfobjecttype: type of the object to be created (used in Identifier class) eg Identifier.PFOBJECTTYPE.ECALCLUSTER
        @param subtype: Identifier subtype, eg 'm' for merged
        @param identifiervalue: The value to be encoded into the Identifier eg energy or pt
 '''
     super(PFObject, self).__init__()
     self.linked = []
     self.locked = False
     self.block_label = None
     self.uniqueid=IdCoder.make_id(pfobjecttype, index, subtype, identifiervalue)
Пример #29
0
 def filter_ids(self, ids, type_and_subtype):
     ''' returns a filtered subset of ids which have a type_and_subtype that matchs the type_and_subtype argument
         eg merged_ecal_ids = filter_ids(ids, 'em')
         @param ids: a list of ids
         @param type_and_subtype: a two letter type and subtype eg 'es' for smeared ecal
     '''
     return [
         uid for uid in ids
         if IdCoder.type_and_subtype(uid) == type_and_subtype
     ]
Пример #30
0
 def id_from_pretty(self, pretty):
     ''' Searches to find the true id given a pretty id string
         Not super efficient but OK for occasional use
         eg uid = self.id_from_pretty('et103')
         @param: pretty is the easily readable name from the Identifier class which is shown in prints and plots eg 'et103'
     '''
     for uid in self.history.keys():
         if IdCoder.pretty(uid) == pretty:
             return uid
     return None
Пример #31
0
 def get_object(self, uniqueid):
     ''' given a uniqueid return the underlying obejct
     '''
     type = IdCoder.get_type(uniqueid)
     subtype = IdCoder.get_subtype(uniqueid)
     if type == IdCoder.PFOBJECTTYPE.TRACK:
         return self.tracks[uniqueid]       
     elif type == IdCoder.PFOBJECTTYPE.ECALCLUSTER:      
         return self.ecal_clusters[uniqueid] 
     elif type == IdCoder.PFOBJECTTYPE.HCALCLUSTER:            
         return self.hcal_clusters[uniqueid]            
     elif type == IdCoder.PFOBJECTTYPE.PARTICLE:
         if subtype == 'g':
             return self.sim_particles[uniqueid]       
         elif subtype == 'r':
             return self.reconstructed_particles[uniqueid]
     elif type == IdCoder.PFOBJECTTYPE.BLOCK:
         return self.blocks[uniqueid]
     else:
         assert(False)   
Пример #32
0
 def __init__(self, pfobjecttype, index, subtype='u', identifiervalue=0.0):
     '''@param pfobjecttype: type of the object to be created (used in Identifier class) eg Identifier.PFOBJECTTYPE.ECALCLUSTER
        @param subtype: Identifier subtype, eg 'm' for merged
        @param identifiervalue: The value to be encoded into the Identifier eg energy or pt
 '''
     super(PFObject, self).__init__()
     self.linked = []
     self.locked = False
     self.block_label = None
     self.uniqueid = IdCoder.make_id(pfobjecttype, index, subtype,
                                     identifiervalue)
Пример #33
0
 def info(self):
     subclusterstr = str('sub(')
     for s in self.subclusters:
         subclusterstr += str('{:}, '.format(IdCoder.pretty(s.uniqueid)))
     subclusterstr += ")"
     return '{energy:7.2f} {theta:5.2f} {phi:5.2f} {sub}'.format(
         energy=self.energy,
         theta=math.pi/2. - self.position.Theta(),
         phi=self.position.Phi(),
         sub=subclusterstr
     )
Пример #34
0
 def get_object(self, uniqueid):
     ''' given a uniqueid return the underlying obejct
     '''
     type = IdCoder.get_type(uniqueid)
     subtype = IdCoder.get_subtype(uniqueid)
     if type == IdCoder.PFOBJECTTYPE.TRACK:
         return self.tracks[uniqueid]
     elif type == IdCoder.PFOBJECTTYPE.ECALCLUSTER:
         return self.ecal_clusters[uniqueid]
     elif type == IdCoder.PFOBJECTTYPE.HCALCLUSTER:
         return self.hcal_clusters[uniqueid]
     elif type == IdCoder.PFOBJECTTYPE.PARTICLE:
         if subtype == 'g':
             return self.sim_particles[uniqueid]
         elif subtype == 'r':
             return self.reconstructed_particles[uniqueid]
     elif type == IdCoder.PFOBJECTTYPE.BLOCK:
         return self.blocks[uniqueid]
     else:
         assert (False)
Пример #35
0
 def reconstruct_block(self, block):
     ''' see class description for summary of reconstruction approach
     '''
     uids = block.element_uniqueids #ids are already stored in sorted order inside block
     self.locked = dict( (uid, False) for uid in uids )
     # first reconstruct muons and electrons
     self.reconstruct_muons(block)
     self.reconstruct_electrons(block)
     # keeping only the elements that have not been used so far
     uids = [uid for uid in uids if not self.locked[uid]]
     if len(uids) == 1: #TODO WARNING!!! LOTS OF MISSING CASES
         uid = uids[0]
         parent_ids = [block.uniqueid, uid]
         if IdCoder.is_ecal(uid):
             self.reconstruct_cluster(self.papasevent.get_object(uid),
                                      "ecal_in", parent_ids)
         elif IdCoder.is_hcal(uid):
             self.reconstruct_cluster(self.papasevent.get_object(uid),
                                      "hcal_in", parent_ids)
         elif IdCoder.is_track(uid):
             self.reconstruct_track(self.papasevent.get_object(uid), 211,
                                    parent_ids)
             
     else: #TODO
         for uid in uids: #already sorted to have higher energy things first (see pfblock)
             if IdCoder.is_hcal(uid):
                 self.reconstruct_hcal(block, uid)
         for uid in uids: #already sorted to have higher energy things first
             if IdCoder.is_track(uid) and not self.locked[uid]:
             # unused tracks, so not linked to HCAL
             # reconstructing charged hadrons.
             # ELECTRONS TO BE DEALT WITH.
                 parent_ids = [block.uniqueid, uid]
                 self.reconstruct_track(self.papasevent.get_object(uid),
                                        211, parent_ids)
                 # tracks possibly linked to ecal->locking cluster
                 for idlink in block.linked_ids(uid, "ecal_track"):
                     #ask colin what happened to possible photons here:
                     self.locked[idlink] = True
                     #TODO add in extra photonsbut decide where they should go?
     self.unused.extend([uid for uid in block.element_uniqueids if not self.locked[uid]])       
Пример #36
0
 def __repr__(self):
     ''' Short Block description
     '''
     description = "block:"
     description += str('{shortname:8} :{prettyid:6}: ecals = {count_ecal} hcals = {count_hcal} tracks = {count_tracks}'.format(
         shortname=self.short_info(),
         prettyid=IdCoder.pretty(self.uniqueid),
         count_ecal=self.count_ecal(),
         count_hcal=self.count_hcal(),
         count_tracks=self.count_tracks())
                       )
     return description
Пример #37
0
 def __repr__(self):
     ''' Short Block description
     '''
     description = "block:"
     description += str(
         '{shortname:8} :{prettyid:6}: ecals = {count_ecal} hcals = {count_hcal} tracks = {count_tracks}'
         .format(shortname=self.short_info(),
                 prettyid=IdCoder.pretty(self.uniqueid),
                 count_ecal=self.count_ecal(),
                 count_hcal=self.count_hcal(),
                 count_tracks=self.count_tracks()))
     return description
Пример #38
0
 def _edge_type(self):
     ''' produces an edge_type string eg "ecal_track"
         the order of id1 an id2 does not matter, 
         eg for one track and one ecal the type will always be "ecal_track" (and never be a "track_ecal")         
     '''
     #consider creating an ENUM instead for the edge_type
     shortid1=IdCoder.type_letter(self.id1);
     shortid2=IdCoder.type_letter(self.id2);
     if shortid1 == shortid2:
         if shortid1 == "h":
             return "hcal_hcal"
         elif shortid1 == "e":
             return "ecal_ecal"
         elif shortid1 == "t":
             return "track_track"           
     elif (shortid1=="h" and shortid2=="t" or shortid1=="t" and shortid2=="h"):
         return "hcal_track"
     elif (shortid1=="e" and shortid2=="t" or shortid1=="t" and shortid2=="e"):
         return "ecal_track"  
     elif (shortid1=="e" and shortid2=="h" or shortid1=="h" and shortid2=="e"):
         return "ecal_hcal"  
     
     return "unknown"
Пример #39
0
    def process(self, event):
        #random.seed(0xdeadbeef) #Useful to make results reproducable between loops and single runs
        event.simulator = self
        event.papasevent = PapasEvent(event.iEv)   
        papasevent = event.papasevent
        gen_particles = getattr(event, self.cfg_ana.gen_particles)
        gen_particles_collection = {} #make a dict from the gen_particles list so that it can be stored into the papasevent collections  
        for g in gen_particles:
            g.set_dagid(IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, g.objid()[0], 'g', g.p4().E()))
            gen_particles_collection[g.dagid()] = g
        def simparticle(ptc, index):
            '''Create a sim particle to be used in papas from an input particle.
            '''
            tp4 = ptc.p4()
            vertex = ptc.start_vertex().position()
            charge = ptc.q()
            pid = ptc.pdgid()
            simptc = Particle(tp4, vertex, charge, pid)
            simptc.set_dagid(IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, index, 's', simptc.idvalue))
            pdebugger.info(" ".join(("Made", simptc.__str__())))
            #simptc.gen_ptc = ptc
            #record that sim particle derives from gen particle
            child = papasevent.history.setdefault(simptc.dagid(), Node(simptc.dagid())) #creates a new node if it is not there already
            parent = papasevent.history.setdefault(ptc.dagid(), Node(ptc.dagid()))
            parent.add_child(child)
            return simptc
        simptcs = [simparticle(ptc, index)
                   for index, ptc in enumerate(gen_particles)]
        try:
            self.simulator.simulate(simptcs, papasevent.history)
        except (PropagationError, SimulationError) as err:
            self.mainLogger.error(str(err) + ' -> Event discarded')
            return False
        #these are the particles before simulation
        simparticles = sorted(self.simulator.ptcs, key=P4.sort_key, reverse=True)
        setattr(event, self.simname, simparticles)
        papasevent.add_collection(gen_particles_collection)
        papasevent.add_collection(self.simulator.simulated_particles)
        papasevent.add_collection(self.simulator.true_tracks)
        papasevent.add_collection(self.simulator.smeared_tracks)
        papasevent.add_collection(self.simulator.smeared_hcals)
        papasevent.add_collection(self.simulator.true_hcals)
        papasevent.add_collection(self.simulator.smeared_ecals)
        papasevent.add_collection(self.simulator.true_ecals)  

        #todo move to separate analyzer
        self.merge_clusters(papasevent) #add to simulator class? 
        #useful when producing outputs from a papasevent
        papasevent.iEv = event.iEv
Пример #40
0
    def test_papasevent(self):
        papasevent = PapasEvent(0)
        ecals = dict()
        tracks = dict()
        mixed = dict()

        for i in range(0, 2):
            uid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.ECALCLUSTER, i, 't',
                                  4.5)
            ecals[uid] = uid
        for i in range(0, 2):
            uid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.TRACK, i, 's', 4.5)
            tracks[uid] = uid

        lastid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.ECALCLUSTER, 3, 't', 3)
        ecals[lastid] = lastid

        papasevent.add_collection(ecals)
        papasevent.add_collection(tracks)

        #check that adding the same collection twice fails
        self.assertRaises(ValueError, papasevent.add_collection, ecals)

        #check that adding a mixed collection fails
        mixed = ecals.copy()
        mixed.update(tracks)
        self.assertRaises(ValueError, papasevent.add_collection, mixed)

        #get we can get back collections OK
        self.assertTrue(len(
            papasevent.get_collection('zz')) == 0)  # this one does not exist
        self.assertTrue(len(papasevent.get_collection('et')) == 3)

        #check get_object
        self.assertTrue(IdCoder.pretty(papasevent.get_object(lastid)) == 'et3')
        self.assertTrue(papasevent.get_object(499) is None)
Пример #41
0
 def simparticle(ptc, index):
     '''Create a sim particle to be used in papas from an input particle.
     '''
     tp4 = ptc.p4()
     vertex = ptc.start_vertex().position()
     charge = ptc.q()
     pid = ptc.pdgid()
     simptc = Particle(tp4, vertex, charge, pid)
     simptc.set_dagid(IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, index, 's', simptc.idvalue))
     pdebugger.info(" ".join(("Made", simptc.__str__())))
     #simptc.gen_ptc = ptc
     #record that sim particle derives from gen particle
     child = papasevent.history.setdefault(simptc.dagid(), Node(simptc.dagid())) #creates a new node if it is not there already
     parent = papasevent.history.setdefault(ptc.dagid(), Node(ptc.dagid()))
     parent.add_child(child)
     return simptc
Пример #42
0
 def reconstruct_cluster(self, cluster, layer, parent_ids,
                         energy=None, vertex=None):
     '''construct a photon if it is an ecal
        construct a neutral hadron if it is an hcal
     '''
     if self.locked[cluster.uniqueid]:
         return 
     if vertex is None:
         vertex = TVector3()
     pdg_id = None
     propagate_to = None
     if layer=='ecal_in':
         pdg_id = 22 #photon
         propagate_to = [ self.detector.elements['ecal'].volume.inner ]
     elif layer=='hcal_in':
         pdg_id = 130 #K0
         propagate_to = [ self.detector.elements['ecal'].volume.inner,
                          self.detector.elements['hcal'].volume.inner ]
     else:
         raise ValueError('layer must be equal to ecal_in or hcal_in')
     assert(pdg_id)
     mass, charge = particle_data[pdg_id]
     if energy is None:
         energy = cluster.energy
     if energy < mass: 
         return None 
     if mass == 0:
         momentum = energy #avoid sqrt for zero mass
     else:
         momentum = math.sqrt(energy**2 - mass**2)
     p3 = cluster.position.Unit() * momentum
     p4 = TLorentzVector(p3.Px(), p3.Py(), p3.Pz(), energy) #mass is not accurate here
     particle = Particle(p4, vertex, charge, pdg_id)
     particle.set_dagid(IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, len(self.particles), 'r', particle.idvalue))
     
     # alice: this may be a bit strange because we can make a photon 
     # with a path where the point is actually that of the hcal?
     # nb this only is problem if the cluster and the assigned layer 
     # are different
     propagator(charge).propagate([particle],
                                  propagate_to)
     #merge Nov 10th 2016 not sure about following line (was commented out in papasevent branch)
     particle.clusters[layer] = cluster  # not sure about this either when hcal is used to make an ecal cluster?
     self.locked[cluster.uniqueid] = True #just OK but not nice if hcal used to make ecal.
     pdebugger.info(str('Made {} from {}'.format(particle, cluster)))
     self.insert_particle(parent_ids, particle)        
Пример #43
0
 def reconstruct_track(self, track, pdgid, parent_ids,
                       clusters=None): # cluster argument does not ever seem to be used at present
     '''construct a charged hadron from the track
     '''
     if self.locked[track.uniqueid]:
         return 
     vertex = track.path.points['vertex']
     pdgid = pdgid * track.charge
     mass, charge = particle_data[pdgid]
     p4 = TLorentzVector()
     p4.SetVectM(track.p3() , mass)
     particle = Particle(p4, vertex, charge, pdgid)
     particle.set_dagid(IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, len(self.particles), 'r', particle.idvalue))
     
     #todo fix this so it picks up smeared track points (need to propagagte smeared track)
     particle.set_track(track) #refer to existing track rather than make a new one
     self.locked[track.uniqueid] = True
     pdebugger.info(str('Made {} from {}'.format(particle, track)))
     self.insert_particle(parent_ids, particle)
     return particle
Пример #44
0
 def simparticle(ptc, index):
     '''Create a sim particle to be used in papas from an input particle.
     '''
     tp4 = ptc.p4()
     vertex = ptc.start_vertex().position()
     charge = ptc.q()
     pid = ptc.pdgid()
     simptc = Particle(tp4, vertex, charge, pid)
     simptc.set_dagid(
         IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, index, 's',
                         simptc.idvalue))
     pdebugger.info(" ".join(("Made", simptc.__str__())))
     #simptc.gen_ptc = ptc
     #record that sim particle derives from gen particle
     child = papasevent.history.setdefault(
         simptc.dagid(), Node(simptc.dagid(
         )))  #creates a new node if it is not there already
     parent = papasevent.history.setdefault(ptc.dagid(),
                                            Node(ptc.dagid()))
     parent.add_child(child)
     return simptc
Пример #45
0
    def __init__(self, element_ids, edges, index, subtype): 
        ''' 
            @param element_ids:  list of the uniqueids of the elements to go in this block [id1,id2,...]
            @param edges: is a dictionary of edges, it must contain at least all needed edges.
                   It is not a problem if it contains
                   additional edges as only the ones needed will be extracted
            @param index: index into the collection of blocks into which new block will be added
            @param subtype: used when making unique identifier, will normally be 'r' for reconstructed blocks and 's' for split blocks
           
        '''
        #make a uniqueid for this block
        self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.BLOCK, index, subtype, len(element_ids))
        #this will sort by type eg ecal, hcal, track and then by energy (biggest first)
        self.element_uniqueids = sorted(element_ids, reverse=True)
        #sequential numbering of blocks, not essential but helpful for debugging
        self.block_count = PFBlock.temp_block_count
        PFBlock.temp_block_count += 1

        #extract the relevant parts of the complete set of edges and store this within the block
        self.edges = dict()
        for id1, id2 in itertools.combinations(self.element_uniqueids, 2):
            key = Edge.make_key(id1, id2)
            self.edges[key] = edges[key]
Пример #46
0
    def __init__(self, element_ids, edges, index, subtype):
        ''' 
            @param element_ids:  list of the uniqueids of the elements to go in this block [id1,id2,...]
            @param edges: is a dictionary of edges, it must contain at least all needed edges.
                   It is not a problem if it contains
                   additional edges as only the ones needed will be extracted
            @param index: index into the collection of blocks into which new block will be added
            @param subtype: used when making unique identifier, will normally be 'r' for reconstructed blocks and 's' for split blocks
           
        '''
        #make a uniqueid for this block
        self.uniqueid = IdCoder.make_id(IdCoder.PFOBJECTTYPE.BLOCK, index,
                                        subtype, len(element_ids))
        #this will sort by type eg ecal, hcal, track and then by energy (biggest first)
        self.element_uniqueids = sorted(element_ids, reverse=True)
        #sequential numbering of blocks, not essential but helpful for debugging
        self.block_count = PFBlock.temp_block_count
        PFBlock.temp_block_count += 1

        #extract the relevant parts of the complete set of edges and store this within the block
        self.edges = dict()
        for id1, id2 in itertools.combinations(self.element_uniqueids, 2):
            key = Edge.make_key(id1, id2)
            self.edges[key] = edges[key]
Пример #47
0
 def __str__(self):
     return '{classname}: {pretty:6}:{uid}: {info}'.format(
         classname=self.__class__.__name__,
         pretty=IdCoder.pretty(self.uniqueid),
         uid=self.uniqueid,
         info=self.info())
Пример #48
0
 def  dagid_str(self):
     if self.dagid() != None:
         return IdCoder.id_str(self.dagid())
     return ""
Пример #49
0
 def count_hcal(self):
     ''' Counts how many hcal cluster ids are in the block '''
     count = 0
     for elem in self.element_uniqueids:
         count += IdCoder.is_hcal(elem)
     return count
Пример #50
0
    def process(self, event):
        #random.seed(0xdeadbeef) #Useful to make results reproducable between loops and single runs
        event.simulator = self
        event.papasevent = PapasEvent(event.iEv)
        papasevent = event.papasevent
        gen_particles = getattr(event, self.cfg_ana.gen_particles)
        gen_particles_collection = {
        }  #make a dict from the gen_particles list so that it can be stored into the papasevent collections
        for g in gen_particles:
            g.set_dagid(
                IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE,
                                g.objid()[0], 'g',
                                g.p4().E()))
            gen_particles_collection[g.dagid()] = g

        def simparticle(ptc, index):
            '''Create a sim particle to be used in papas from an input particle.
            '''
            tp4 = ptc.p4()
            vertex = ptc.start_vertex().position()
            charge = ptc.q()
            pid = ptc.pdgid()
            simptc = Particle(tp4, vertex, charge, pid)
            simptc.set_dagid(
                IdCoder.make_id(IdCoder.PFOBJECTTYPE.PARTICLE, index, 's',
                                simptc.idvalue))
            pdebugger.info(" ".join(("Made", simptc.__str__())))
            #simptc.gen_ptc = ptc
            #record that sim particle derives from gen particle
            child = papasevent.history.setdefault(
                simptc.dagid(), Node(simptc.dagid(
                )))  #creates a new node if it is not there already
            parent = papasevent.history.setdefault(ptc.dagid(),
                                                   Node(ptc.dagid()))
            parent.add_child(child)
            return simptc

        simptcs = [
            simparticle(ptc, index) for index, ptc in enumerate(gen_particles)
        ]
        try:
            self.simulator.simulate(simptcs, papasevent.history)
        except (PropagationError, SimulationError) as err:
            self.mainLogger.error(str(err) + ' -> Event discarded')
            return False
        #these are the particles before simulation
        simparticles = sorted(self.simulator.ptcs,
                              key=P4.sort_key,
                              reverse=True)
        setattr(event, self.simname, simparticles)
        papasevent.add_collection(gen_particles_collection)
        papasevent.add_collection(self.simulator.simulated_particles)
        papasevent.add_collection(self.simulator.true_tracks)
        papasevent.add_collection(self.simulator.smeared_tracks)
        papasevent.add_collection(self.simulator.smeared_hcals)
        papasevent.add_collection(self.simulator.true_hcals)
        papasevent.add_collection(self.simulator.smeared_ecals)
        papasevent.add_collection(self.simulator.true_ecals)

        #todo move to separate analyzer
        self.merge_clusters(papasevent)  #add to simulator class?
        #useful when producing outputs from a papasevent
        papasevent.iEv = event.iEv
Пример #51
0
 def short_info(self, node):
     '''used to label plotted dag nodes
     @param node: a node in the DAG history'''
     obj = self.object(node)
     return IdCoder.pretty(obj.uniqueid) + "\n " + obj.short_info()
Пример #52
0
 def type_and_subtype(self, node):
     ''' Return two letter type and subtype code for a node For example 'pg', 'ht' etc
     @param node: a node in the DAG history'''
     return IdCoder.type_and_subtype(node.get_value())
Пример #53
0
 def pretty(self, node):
     ''' pretty form of the unique identifier
     @param node: a node in the DAG history'''
     return IdCoder.pretty(node.get_value())
Пример #54
0
    def test_1(self):
        IdCoder.reset()
        event  =  Event(distance)
        sim  =  Simulator(event)
        event=sim.event
        
        pfblocker = PFBlockBuilder(event, event.history.keys(), distance)
        
        event.blocks = pfblocker.blocks
        #event.history = pfblocker.history
        
        
        ##test block splitting
        #blockids = []
        #unlink=[]
        #for b in event.blocks.itervalues():
            #ids=b.element_uniqueids
            #if len(ids)==3 :
                #print ids[0], ids[2]
                #unlink.append(b.edges[Edge.make_key(ids[0], ids[2])])
                #unlink.append(b.edges[Edge.make_key(ids[0], ids[1])])
                #print unlink
                #splitter=BlockSplitter(b,unlink,event.history)
                #print splitter.blocks
        
        #blocksplitter=BlockSplitter()
       
        rec  =  Reconstructor(event)

        
        # What is connected to HCAL 202 node?
        #  (1) via history_nodes
        #  (2) via reconstructed node links
        #  (3) Give me all blocks with  one track:
        #  (4) Give me all simulation particles attached to each reconstructed particle
        nodeid = 202
        nodeuid = sim.UID(nodeid)
        
        #(1) what is connected to the the HCAL CLUSTER
        ids = []
        BFS  =  BreadthFirstSearchIterative(event.history[nodeuid],"undirected")
        for n in BFS.result :
            ids.append(n.get_value())
         
        #1b WHAT BLOCK Does it belong to   
        x = None
        for uid in ids:
            if IdCoder.is_block(uid) and event.blocks[uid].short_info()== "E1H1T1":
                x =  event.blocks[uid]     
                
        #1c #check that the block contains the expected list of suspects    
        pids = [] 
        for n in x.element_uniqueids:
            pids.append(n)              
        ids  = sorted(pids)
        expected_ids = sorted([sim.UID(2), sim.UID(102),sim.UID(202), sim.UID(302)])
        self.assertEqual(ids,expected_ids )
    
        #(2) use edge nodes to see what is connected
        ids = []
        BFS  =  BreadthFirstSearchIterative(pfblocker.nodes[nodeuid],"undirected")
        for n in BFS.result :
            ids.append(n.get_value())
        expected_ids = sorted([sim.UID(2), sim.UID(102),sim.UID(202),sim.UID(302)])   
        self.assertEqual(sorted(ids), expected_ids)

        #(3) Give me all simulation particles attached to each reconstructed particle
        for rp in event.reconstructed_particles :
            ids=[]
            BFS  =  BreadthFirstSearchIterative(event.history[rp],"parents")    
            for n in BFS.result :
                z=n.get_value()