lcsim/src/org/lcsim/recon/cluster/clumpfinder
diff -N ClumpFinder.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ClumpFinder.java 17 Jan 2006 00:11:05 -0000 1.1
@@ -0,0 +1,170 @@
+package org.lcsim.recon.cluster.clumpfinder;
+
+import java.lang.String;
+import java.util.List;
+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 org.lcsim.util.decision.*;
+import org.lcsim.recon.cluster.util.ClusterSizeDecision;
+
+/**
+ * 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.1 2006/01/17 00:11:05 mcharles Exp $
+ */
+
+public class ClumpFinder extends Driver
+{
+ protected String m_inputHitMapName;
+ protected String m_outputClusterListName;
+ protected String m_outputHitMapName;
+ protected DecisionMakerSingle<CalorimeterHit> m_hitDecision;
+ protected DecisionMakerSingle<CalorimeterHit> m_seedDecision;
+ protected DecisionMakerSingle<Cluster> m_clumpDecision;
+
+ /**
+ * 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 inputHitMap, String outputClusterList, String outputHitMap)
+ {
+ m_inputHitMapName = inputHitMap;
+ m_outputClusterListName = outputClusterList;
+ m_outputHitMapName = outputHitMap;
+
+ m_hitDecision = new DummyDecisionMakerSingle<CalorimeterHit> (); // Default: All hits are valid
+ m_seedDecision = new DummyDecisionMakerSingle<CalorimeterHit> (); // Default: All seeds are valid
+ m_clumpDecision = new ClusterSizeDecision(6); // Default: Clumps must have >= 6 hits
+ }
+
+
+ /**
+ * Find clumps within each cluster, and write them back to the event.
+ */
+ public void process(EventHeader event)
+ {
+ // Read in the inputs
+ Map<Long,CalorimeterHit> inputHitMap = (Map<Long,CalorimeterHit>) (event.get(m_inputHitMapName));
+
+ // Outputs:
+ Vector<Cluster> outputClusterList = new Vector<Cluster>();
+ Map<Long,CalorimeterHit> outputHitMap = new HashMap<Long,CalorimeterHit>(inputHitMap); // initially cloned
+
+ // Clone the input hitmap to give a list of unused hits we can edit:
+ ListFilter<CalorimeterHit> hitFilter = new ListFilter<CalorimeterHit> (m_hitDecision);
+ List<CalorimeterHit> inputHits = hitFilter.filterList(inputHitMap.values());
+ // Filter these again, require high local density:
+ ListFilter<CalorimeterHit> densityFilter = new ListFilter<CalorimeterHit> (new HighHitDensityDecision(inputHits));
+ List<CalorimeterHit> unusedHits = densityFilter.filterList(inputHits);
+ // 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
+ outputClusterList.add(clump);
+ for (CalorimeterHit hitInClump : clump.getCalorimeterHits()) {
+ unusedHits.remove(hitInClump);
+ outputHitMap.remove(hitInClump.getCellID());
+ }
+ }
+ }
+ }
+
+ // Write out:
+ event.put(m_outputHitMapName, outputHitMap);
+ event.put(m_outputClusterListName, outputClusterList);
+ }
+
+ /**
+ * 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;
+ }
+ }
+ }
+ }
+ for (CalorimeterHit acceptedNeighbourHit : neighbourHits) {
+ // Add this to the clump:
+ clump.addHit(acceptedNeighbourHit);
+ hitsUsedInClump.add(acceptedNeighbourHit);
+ }
+ for (CalorimeterHit acceptedNeighbourHit : neighbourHits) {
+ // Add its neighbours....
+ recursivelyAddNeighbours(acceptedNeighbourHit, clump, unusedHits, hitsUsedInClump);
+ }
+ }
+ }
+}
lcsim/src/org/lcsim/recon/cluster/clumpfinder
diff -N HighHitDensityDecision.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ HighHitDensityDecision.java 17 Jan 2006 00:11:05 -0000 1.1
@@ -0,0 +1,85 @@
+package org.lcsim.recon.cluster.clumpfinder;
+
+import java.lang.String;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+import java.util.HashMap;
+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 org.lcsim.util.decision.*;
+
+/**
+ * A DecisionMaker for CalorimeterHit objects which requires them to
+ * have a high local hit density. Right now the values are hard-coded.
+ * Eventually this should be made more flexible and moved outside
+ * the "structural" package.
+ *
+ * @version $Id: HighHitDensityDecision.java,v 1.1 2006/01/17 00:11:05 mcharles Exp $
+ */
+
+public class HighHitDensityDecision implements DecisionMakerSingle<CalorimeterHit>
+{
+ /**
+ * Constructor.
+ *
+ * @param hits The list of hits to be used for the local density calcualtion
+ */
+ public HighHitDensityDecision(Collection<CalorimeterHit> hits) {
+ // Initialize
+ m_hitIDSet = new HashSet<Long> ();
+ for (CalorimeterHit hit : hits) {
+ m_hitIDSet.add(new Long(hit.getCellID()));
+ }
+ }
+
+ /**
+ * Check if the CalorimeterHit has a high local hit density.
+ * Currently hard-coded to look in a 5x5x3 grid and require
+ * at least 6 other hits present.
+ */
+ public boolean valid(CalorimeterHit seedHit)
+ {
+ // Find hits within m_dist:
+ 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 = 2;
+ int deltaPhi = 2;
+ int minNeighbours = 6;
+ // Find all hits from [unusedHits] within range:
+ long[] neighbourIDs = idCAL.getNeighbourIDs(deltaLayer, deltaTheta, deltaPhi);
+ Set<Long> acceptedNeighbourIDs = new HashSet<Long>();
+ for (long neighbourID : neighbourIDs) {
+ if (m_hitIDSet.contains(new Long(neighbourID))) {
+ acceptedNeighbourIDs.add(neighbourID);
+ }
+ }
+ // Were there enough?
+ if (acceptedNeighbourIDs.size() >= minNeighbours) {
+ // Accept this
+ return true;
+ } else {
+ // Reject this
+ return false;
+ }
+ }
+ }
+
+ protected Set<Long> m_hitIDSet = null;
+
+}