Commit in lcsim/src/org/lcsim/contrib/uiowa/structural on MAIN
ClumpFinder.java+161-51.1 -> 1.2
Bugfix to prevent double-counting of seed hit in clump; improvements to make code more modular.

lcsim/src/org/lcsim/contrib/uiowa/structural
ClumpFinder.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- ClumpFinder.java	14 Oct 2005 17:54:30 -0000	1.1
+++ ClumpFinder.java	19 Oct 2005 19:44:21 -0000	1.2
@@ -5,30 +5,186 @@
 import java.util.Map;
 import java.util.Vector;
 import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
 
 import org.lcsim.event.EventHeader;
 import org.lcsim.util.Driver;
 import org.lcsim.event.Cluster;
+import org.lcsim.recon.cluster.util.BasicCluster;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.geometry.IDDecoder;
+
+import util.decision.*; //import org.lcsim.util.decision.*;
+
+/**
+ * A Driver which finds dense clumps within clusters.
+ * Each event, it reads in a List<Cluster> from the EventHeader.
+ * For each Cluster in that list, it tries to find dense
+ * clumps. Currently, clumps are defined as contiguous sets of
+ * 6+ hits where the local hit density (in a 5x5x3 block) is 7/75
+ * or more for each hit. This is hard-coded at the moment, but
+ * should become a user-definable in a later version.
+ * 
+ * @version $Id: ClumpFinder.java,v 1.2 2005/10/19 19:44:21 mcharles Exp $
+ */
 
 public class ClumpFinder extends Driver
 {
     protected String m_clumpMapName;
     protected String m_trackMapName;
     protected String m_clustersName;
-    
+    protected DecisionMakerSingle<CalorimeterHit> m_seedDecision;
+    protected DecisionMakerSingle<Cluster> m_clumpDecision;
+    protected EventHeader m_event;
+
+    /**
+     * Constructor.
+     * @param clustersName Name of the input List<Cluster> in the event
+     * @param trackSegmentsName Name of the map which gives a list of track segments for each Cluster. These will not be used in clump-finding.
+     * @param outputName Name to use for the output map which will give a list of clumps for each Cluster.
+     */
     public ClumpFinder(String clustersName, String trackSegmentsName, String outputName)
     {
 	m_clumpMapName = outputName;
 	m_trackMapName = trackSegmentsName;
 	m_clustersName = clustersName;
+	m_seedDecision = new DummyDecisionMakerSingle<CalorimeterHit> (); // Default: All seeds are valid
+	m_clumpDecision = new ClusterSizeDecision(6); // Default: Clumps must have >= 6 hits
+	m_event = null;
     }
 
-    public void process(EventHeader event) {
-	Map<Cluster, List<Cluster> > mapClustersToClumps = new HashMap<Cluster, List<Cluster> > ();
+
+    /**
+     * Find clumps within each cluster, and write them back to the event.
+     */
+    public void process(EventHeader event) 
+    {
+	m_event = event;
+	
+	// The input is a list of big clusters:
+	List<Cluster> inputList = event.get(Cluster.class, m_clustersName);
+	// The output is a map from a cluster (the big cluster) to a list of clusters (the clumps in that big cluster)
+	MapClusterToListOfClusters outputMap = new MapClusterToListOfClusters();
+	// We also use a map from clusters to track segments:
+	List<MapClusterToListOfClusters> inputTrackMapList = event.get(MapClusterToListOfClusters.class, m_trackMapName);
+	MapClusterToListOfClusters inputTrackMap = null;
+	if (inputTrackMapList != null) {
+	    if (inputTrackMapList.iterator().hasNext()) {
+		inputTrackMap = inputTrackMapList.iterator().next();
+	    }
+	}
+
+	for (Cluster bigCluster : inputList) {
+	    // Handle each big cluster. Here is the list of clumps:
+	    Vector<Cluster> clumps = new Vector<Cluster>();
+	    // To begin with, put all hits in the cluster into a collection of unused hits:
+	    Vector<CalorimeterHit> unusedHits = new Vector<CalorimeterHit>();
+	    unusedHits.addAll(bigCluster.getCalorimeterHits());
+	    // Veto any hits which are part of a track segment:
+	    List<Cluster> trackSegments = inputTrackMap.get(bigCluster);
+	    if (trackSegments != null) {
+		for (Cluster trackSegment : trackSegments) {
+		    for (CalorimeterHit trackHit : trackSegment.getCalorimeterHits()) {
+			unusedHits.remove(trackHit);
+		    }
+		}
+	    }
+	    // Apply some pre-filtering on these with a ListFilter to get a list of seeds
+	    ListFilter<CalorimeterHit> seedFilter = new ListFilter<CalorimeterHit> (m_seedDecision);
+	    List<CalorimeterHit> seedHits = seedFilter.filterList(unusedHits);
+	    // For each seed hit, try to make a clump
+	    for (CalorimeterHit seedHit : seedHits) {
+		// This seed hit might already have been removed if it was used in a previous
+		// clump, so we need to verify it's still there.
+		if (unusedHits.contains(seedHit)) {
+		    Cluster clump = makeClump(seedHit, unusedHits);
+		    if (m_clumpDecision.valid(clump)) {
+			// Accept this clump -- add it to the output list and remove its hits from consideration
+			clumps.add(clump);
+			for (CalorimeterHit hitInClump : clump.getCalorimeterHits()) {
+			    unusedHits.remove(hitInClump);
+			}
+		    }
+		}
+	    }
+	    // Done with this big cluster -- add its clumps to the output map:
+	    outputMap.put(bigCluster, clumps);
+	}
 	// Dummy for writeout
-	List<Map<Cluster, List<Cluster>>> dummyList = new Vector<Map<Cluster, List<Cluster> > > ();
-	dummyList.add(mapClustersToClumps);
+	List<MapClusterToListOfClusters> dummyList = new Vector<MapClusterToListOfClusters> ();
+	dummyList.add(outputMap);
 	event.put(m_clumpMapName, dummyList);
     }
 
+    /**
+     * Interal routine: make a clump.
+     *
+     * @param seedHit    The first hit in the clump
+     * @param unusedHits The list of hits which may be added to the clump. This contains seedHit.
+     */
+    protected Cluster makeClump(CalorimeterHit seedHit, List<CalorimeterHit> unusedHits) 
+    {
+	// This we expect to be over-ridden. Might even farm it out to
+	// an external class. For now, here is a default.
+
+	BasicCluster clump = new BasicCluster();
+	Set<CalorimeterHit> hitsUsedInClump = new HashSet<CalorimeterHit>();
+	clump.addHit(seedHit);
+	hitsUsedInClump.add(seedHit);
+	recursivelyAddNeighbours(seedHit, clump, unusedHits, hitsUsedInClump);
+	return clump;
+    }
+
+    /**
+     * Internal routine: add hits to a clump.
+     *
+     * @param seedHit         Try to add neighbours of this hit
+     * @param clump           The clump
+     * @param unusedHits      The list of hits which may be added to the clump. This contains seedHit.
+     * @param hitsUsedInClump The subset of unusedHits which have already been added to the clump.
+     */
+    protected void recursivelyAddNeighbours(CalorimeterHit seedHit, BasicCluster clump, List<CalorimeterHit> unusedHits, Set<CalorimeterHit> hitsUsedInClump)
+    {
+	org.lcsim.geometry.IDDecoder id = seedHit.getIDDecoder();
+	org.lcsim.geometry.CalorimeterIDDecoder idCAL = (org.lcsim.geometry.CalorimeterIDDecoder) (id);
+	idCAL.setID(seedHit.getCellID());
+	if (!idCAL.supportsNeighbours()) {
+	    throw new AssertionError("Can't get neighbours!");
+	} else {
+	    // Parameters (hard-coded for now);
+	    int deltaLayer = 1;
+	    int deltaTheta = 1;
+	    int deltaPhi = 1;
+	    // Find all hits from [unusedHits] within range:
+	    long[] neighbourIDs = idCAL.getNeighbourIDs(deltaLayer, deltaTheta, deltaPhi);
+	    List<CalorimeterHit> neighbourHits = new Vector<CalorimeterHit>();
+	    // Look for cells which
+	    //   (a) are neighbours
+	    //   (b) are in the list "unusedHit", i.e. have a hit and aren't already in a clump/track/etc
+	    //   (c) aren't in the set "hitsUsedInClump", to avoid double-counting
+	    for (long neighbourID : neighbourIDs) {
+		for (CalorimeterHit unusedHit : unusedHits) {
+		    if (unusedHit.getCellID() == neighbourID) {
+			if ( ! hitsUsedInClump.contains(unusedHit) ) {
+			    neighbourHits.add(unusedHit);
+			    break;
+			}
+		    }
+		}
+	    }
+	    // Check each of these for validity...
+	    ListFilter<CalorimeterHit> filter = new ListFilter<CalorimeterHit> (new HighHitDensityDecision(unusedHits));
+	    List<CalorimeterHit> acceptedNeighbourHits = filter.filterList(neighbourHits);
+	    for (CalorimeterHit acceptedNeighbourHit : acceptedNeighbourHits) {
+		// Add this to the clump:
+		clump.addHit(acceptedNeighbourHit);
+		hitsUsedInClump.add(acceptedNeighbourHit);
+	    }
+	    for (CalorimeterHit acceptedNeighbourHit : acceptedNeighbourHits) {
+		// Add its neighbours....
+		recursivelyAddNeighbours(acceptedNeighbourHit, clump, unusedHits, hitsUsedInClump);
+	    }
+	}
+    }
 }
CVSspam 0.2.8