lcsim/src/org/lcsim/contrib/uiowa/structural
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);
+ }
+ }
+ }
}