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
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
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"
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
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)
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)
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'))
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
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
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
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'))
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
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
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
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
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)
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]
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
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
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())]
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
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)
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
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)
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)
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)
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 ]
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)
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)
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 )
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)
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]])
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
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
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"
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
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)
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
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)
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
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
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]
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())
def dagid_str(self): if self.dagid() != None: return IdCoder.id_str(self.dagid()) return ""
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
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
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()
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())
def pretty(self, node): ''' pretty form of the unique identifier @param node: a node in the DAG history''' return IdCoder.pretty(node.get_value())
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()