def _make_edge(self, id1, id2, ruler): ''' id1, id2 are the unique ids of the two items ruler is something that measures distance between two objects eg track and hcal (see Distance class for example) it should take the two objects as arguments and return a tuple of the form link_type = 'ecal_ecal', 'ecal_track' etc is_link = true/false distance = float an edge object is returned which contains the link_type, is_link (bool) and distance between the objects. ''' #find the original items and pass to the ruler to get the distance info obj1 = self.pfevent.get_object(id1) obj2 = self.pfevent.get_object(id2) link_type, is_linked, distance = ruler( obj1, obj2 ) #some redundancy in link_type as both distance and Edge make link_type #not sure which to get rid of #for the event we do not want ehal_hcal links if link_type == "ecal_hcal": is_linked = False #make the edge return Edge(id1, id2, is_linked, distance)
def __init__(self, element_ids, edges, pfevent): ''' element_ids: list of the uniqueids of the elements to go in this block [id1,id2,...] 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 pfevent: allows access to the underlying elements given a uniqueid must provide a get_object function ''' #make a uniqueid for this block self.uniqueid = Identifier.make_id(Identifier.PFOBJECTTYPE.BLOCK) self.is_active = True # if a block is subsequently split it will be deactivated #allow access to the underlying objects self.pfevent = pfevent #comment out energy sorting for now as not available C++ sortby = lambda x: Identifier.type_short_code(x) self.element_uniqueids = sorted(element_ids, key=sortby) #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 __init__(self, element_ids, edges, pfevent): ''' element_ids: list of the uniqueids of the elements to go in this block [id1,id2,...] 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 pfevent: allows access to the underlying elements given a uniqueid must provide a get_object function ''' #make a uniqueid for this block self.uniqueid = Identifier.make_id(Identifier.PFOBJECTTYPE.BLOCK) self.is_active = True # if a block is subsequently split it will be deactivated #allow access to the underlying objects self.pfevent=pfevent #order the elements by element type (ecal, hcal, track) and then by energy #this is a bit yucky but needed to make sure the order returned is consistent #maybe should live outside of this class self.element_uniqueids = sorted(element_ids, key = lambda x: (Identifier.type_short_code(x),-self.pfevent.get_object(x).energy) ) #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 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 Identifier.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 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 __init__(self, element_ids, edges, subtype): ''' element_ids: list of the uniqueids of the elements to go in this block [id1,id2,...] 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 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 = Identifier.make_id(Identifier.PFOBJECTTYPE.BLOCK, subtype) self.element_uniqueids = sorted( element_ids, key=lambda x: Identifier.type_letter(x)) #comment out energy sorting for now as not available C++ sortby = lambda x: Identifier.type_letter(x) self.element_uniqueids = sorted(element_ids, key=sortby) #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 __init__(self, clusters, ruler, history_nodes = None): ''' clusters a dictionary : {id1:ecal1, id2:ecal2, ...} ruler is something that measures distance between two objects eg track and hcal (see Distance class for example) it should take the two objects as arguments and return a tuple of the form link_type = 'ecal_ecal', 'ecal_track' etc is_link = true/false distance = float hist_nodes is an optional dictionary of Nodes : { id:Node1, id: Node2 etc} it could for example contain the simulation history nodes A Node contains the id of an item (cluster, track, particle etc) and says what it is linked to (its parents and children) if hist_nodes is provided it will be added to with the new block information If hist_nodes is not provided one will be created, it will contain nodes corresponding to each of the tracks, ecal etc and also for the blocks that are created by the event block builder. ''' self.clusters = clusters # the merged clusters will be stored here self.merged = dict() # collate ids of clusters uniqueids = list(clusters.keys()) #make the edges match cpp by using the same approach as cpp edges = dict() for obj1 in clusters.values(): for obj2 in clusters.values(): if obj1.uniqueid < obj2.uniqueid : link_type, is_linked, distance = ruler(obj1, obj2) edge = Edge(obj1.uniqueid, obj2.uniqueid, is_linked, distance) #the edge object is added into the edges dictionary edges[edge.key] = edge #make the subgraphs of clusters super(MergedClusterBuilder, self).__init__(uniqueids, edges) #make sure we use the original history and update it as needed self.history_nodes = history_nodes self._make_merged_clusters()
def __init__(self, clusters, ruler, history_nodes): ''' @param clusters: a dictionary : {id1:ecal1, id2:ecal2, ...}. @param ruler: measures distance between two clusters, see Distance class for example. It should take the two objects as arguments and return a tuple of the form: link_type = 'ecal_ecal' is_link = true/false distance = float @param history_nodes: a dictionary of Nodes : { id:Node1, id: Node2 etc}. It could for example contain the simulation history nodes. A Node contains the id of a cluster. and says what it is linked to (its parents and children). New mergedcluster history detailing which clusters the mergedcluster was made from will be added to the existing history ''' self.clusters = clusters # the merged clusters will be stored here self.merged_clusters = dict() # collate ids of clusters uniqueids = list(clusters.keys()) #make the edges match cpp by using the same approach as cpp edges = dict() for obj1 in clusters.values(): for obj2 in clusters.values(): if obj1.uniqueid < obj2.uniqueid: link_type, is_linked, distance = ruler(obj1, obj2) edge = Edge(obj1.uniqueid, obj2.uniqueid, is_linked, distance) #the edge object is added into the edges dictionary edges[edge.key] = edge #make the subgraphs of clusters super(MergedClusterBuilder, self).__init__(uniqueids, edges) #make sure we use the original history and update it as needed self.history_nodes = history_nodes self._make_and_store_merged_clusters()
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 __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 = Identifier.make_id(Identifier.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 get_edge(self, id1, id2): ''' Find the edge corresponding to e1 e2 Note that make_key deals with whether it is get_edge(e1, e2) or get_edge(e2, e1) (either order gives same result) ''' return self.edges[Edge.make_key(id1, id2)]
def get_edge(self, id1, id2): ''' Find the edge corresponding to e1 e2 Note that make_key deals with whether it is get_edge(e1, e2) or get_edge(e2, e1) (either order gives same result) ''' return self.edges[Edge.make_key(id1,id2)]