Print

Print


Commit in lcsim/src/org/lcsim/recon/cluster/mst on MAIN
GeometricalDistance.java+36added 1.1
MSTClusterBuilder.java+136added 1.1
MSTClusterDriver.java+159added 1.1
Metrics.java+20added 1.1
MinimumHitToHitDistance.java+46added 1.1
+397
5 added files
MST clusterer

lcsim/src/org/lcsim/recon/cluster/mst
GeometricalDistance.java added at 1.1
diff -N GeometricalDistance.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ GeometricalDistance.java	2 Jan 2006 21:17:03 -0000	1.1
@@ -0,0 +1,36 @@
+package org.lcsim.recon.cluster.mst; 
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.event.Cluster;
+
+/**
+  * A distance metric that computes the 3D distance
+  * between two clusters.
+  */
+public class GeometricalDistance implements Metrics {
+
+    /**
+      * Simple constructor
+      */
+    public GeometricalDistance(){}
+
+    /**
+      * Compute the 3D distance between two clusters. Each cluster's
+      * position is taken as Cluster.getPosition(), which is usually
+      * the center of energy. The method then returns the distance
+      * between the two position points.
+      */
+    public double getDistance (Cluster cluster1, Cluster cluster2){
+        double[] coOne = cluster1.getPosition();
+        double[] coTwo = cluster2.getPosition();
+
+        double distance = Math.sqrt((coOne[0]-coTwo[0])*(coOne[0]-coTwo[0])
+                                  + (coOne[1]-coTwo[1])*(coOne[1]-coTwo[1])
+                                  + (coOne[2]-coTwo[2])*(coOne[2]-coTwo[2]));
+
+        return distance;
+    }
+
+}

lcsim/src/org/lcsim/recon/cluster/mst
MSTClusterBuilder.java added at 1.1
diff -N MSTClusterBuilder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MSTClusterBuilder.java	2 Jan 2006 21:17:03 -0000	1.1
@@ -0,0 +1,136 @@
+package org.lcsim.recon.cluster.mst; 
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.Cluster;
+import org.lcsim.recon.cluster.util.BasicCluster;
+import org.lcsim.recon.cluster.util.ClusterNotEmptyDecisionMaker;
+
+import org.lcsim.util.decision.*;
+
+/**
+  * A class to build clusters using the Minimum Spanning Tree (MST)
+  * algorithm.
+  */
+public class MSTClusterBuilder {
+
+    /**
+      * Constructor.
+      *  @param list The objects that will be clustered. If these are hits, they must be pre-wrapped into Cluster format.
+      */
+    public MSTClusterBuilder (List<Cluster> list, EventHeader event){
+	inputList = list;
+        thisEvent = event;
+    }
+
+    /**
+      * Main clustering routine. Any two objects where metric(object1,object2)<threshold
+      * will be placed in the same output cluster (though the converse is not necessarily true).
+      * In addition, three decision makers are checked for validity (seedDecision, pairDecision, outputDecision).
+      */
+    public List<Cluster> doClustering(){
+	List<Cluster> outputList = new ArrayList<Cluster>();
+	usedList.clear();
+
+	if (inputList == null ) throw new AssertionError ("empty inputList");
+
+	if (theMetrics == null ) throw new AssertionError ("no metrics");
+
+	if ( !thresholdSet ) throw new AssertionError ("no threshold");
+
+	Iterator iter = inputList.iterator();
+	while (iter.hasNext()){
+	    Cluster inOne = (Cluster) iter.next();
+	    if (!usedList.contains(inOne) && seedDecision.valid(inOne)){
+		BasicCluster out = new BasicCluster();
+		recursiveClusterer(inOne, out);
+		if (outputDecision.valid(out)) {
+		    outputList.add(out);
+		}
+	    }
+	}
+	return outputList;
+    }
+
+    /**
+      * An internal routine for clustering
+      */
+    protected void recursiveClusterer(Cluster inOne, BasicCluster out){
+	out.addCluster(inOne);
+	usedList.add(inOne);
+	Iterator iter = inputList.iterator();
+	while (iter.hasNext()){
+	    Cluster inTwo = (Cluster) iter.next();
+	    if (!usedList.contains(inTwo)){
+		if (pairDecision.valid(inOne, inTwo)){
+		    double d = theMetrics.getDistance(inOne, inTwo);
+		    if (d < threshold){
+			recursiveClusterer(inTwo, out);
+		    }
+		}
+	    }
+	}
+	return;
+    }
+
+    protected List<Cluster> inputList;
+    protected Collection<Cluster> usedList = new HashSet<Cluster>();
+
+    protected Metrics theMetrics = null;
+    protected double threshold;
+    protected boolean thresholdSet = false;
+    protected DecisionMakerSingle<Cluster> seedDecision = new ClusterNotEmptyDecisionMaker();
+    protected DecisionMakerPair<Cluster,Cluster> pairDecision = new ClusterNotEmptyDecisionMaker();
+    protected DecisionMakerSingle<Cluster> outputDecision = new ClusterNotEmptyDecisionMaker();
+
+    protected EventHeader thisEvent = null;
+
+    /**
+      * Specify the metric to be used. This must be done
+      * before doClustering() can be called.
+      */
+    public void registerMetrics(Metrics m){
+	theMetrics = m;
+    }
+
+    /**
+      * Specify the threshold to be used. This must be done
+      * before doClustering() can be called.
+      */
+    public void setThreshold(double t){
+	threshold = t;
+	thresholdSet = true;
+    }
+
+    /**
+      * Specify the seed decision. For an object to be the first
+      * (or only) object in a cluster, it must pass the seed decision.
+      * Objects do not pass the seed decision will either be picked up
+      * in a cluster as it spreads, or will not appear in any cluster.
+      */
+    public void setSeedDecision(DecisionMakerSingle<Cluster> d){
+	seedDecision = d;
+    }
+
+    /**
+      * Specify the pair decision. To link two objects, the pair decision
+      * must be valid AND metric(object1,object2)<threshold. If either
+      * condition fails there will not be a direct link, though it is still
+      * possible for both to end up in the same cluster (e.g. if both are
+      * successfully linked to a third object).
+      */
+    public void setPairDecision(DecisionMakerPair<Cluster,Cluster> d){
+	pairDecision = d;
+    }
+
+    /**
+      * Specify the output decision. For a cluster to be written out to the
+      * output list, this decision must be valid.
+      */
+    public void setOutputDecision(DecisionMakerSingle<Cluster> d){
+	outputDecision = d;
+    }
+
+}

lcsim/src/org/lcsim/recon/cluster/mst
MSTClusterDriver.java added at 1.1
diff -N MSTClusterDriver.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MSTClusterDriver.java	2 Jan 2006 21:17:03 -0000	1.1
@@ -0,0 +1,159 @@
+package org.lcsim.recon.cluster.mst; 
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.util.Driver;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.CalorimeterHit;
+
+import org.lcsim.recon.cluster.util.*;
+import org.lcsim.util.decision.*;
+
+/**
+  * A convenience class which wraps the MSTClusterBuilder in a Driver.
+  */
+public class MSTClusterDriver extends Driver {
+
+    private double threshold = 30.0;
+    private String calType = "EMCal";
+    private String clusterName = "MSTCluster "+calType;
+    private Metrics metrics = new GeometricalDistance();
+    private DecisionMakerSingle<Cluster> inputDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerSingle<Cluster> seedDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerPair<Cluster,Cluster> pairDecision = new ClusterNotEmptyDecisionMaker();
+    private DecisionMakerSingle<Cluster> outputDecision = new ClusterNotEmptyDecisionMaker();
+    private List<String> listToGet;
+
+  /**
+    * Constructor. Takes a string parameter which controls the behaviour.
+    * Allowed strings are "EMCal", "HCal", and "User".
+    * "EMCal" and "HCal" instruct the driver to try to find the lists of hits
+    * in the electromagnetic or hadronic calorimeters, respectively.
+    * "User" indicates that the user will specify all of the lists.
+    * For all three strings, additional lists may be specified with the 
+    * addUserInputList() method.
+    *
+    * @param inString Calorimeter type (see above)
+    */
+    public MSTClusterDriver(String inString){
+        calType = inString;
+        listToGet = new ArrayList<String>();
+        if ( calType == "EMCal" || calType == "all") {
+            listToGet.add("EcalBarrHits");
+	    listToGet.add("EcalEndcapHits");
+        } else if ( calType == "HCal" || calType == "all") {
+	    listToGet.add("HcalBarrHits");
+            listToGet.add("HcalEndcapHits");
+        } else if ( calType == "User" ) {
+        } else {
+            throw new AssertionError("Calorimeter type '"+calType+"' not recognized.");
+        }
+    }
+
+  /**
+    * Process one event.
+    */
+    public void process(EventHeader event){
+        List<Cluster> inputList = new ArrayList<Cluster> ();
+
+        for (String s : listToGet) {
+          ObjectToClusterWrapper wrap = new BasicObjectToClusterWrapper();
+          List l = event.get(Object.class, s);
+          try {
+            List<Cluster> wrappedList = wrap.wrapListOfObjects(l);
+            inputList.addAll(wrappedList);
+          } catch (UnwrappableObjectException x) {
+            // We fed it an  object it didn't understand (not a Cluster or CalorimeterHit)
+            throw new AssertionError(x);
+          }
+	}
+        ListFilter<Cluster> filter = new ListFilter<Cluster> (inputDecision);
+	List<Cluster> list = filter.filterList(inputList);
+
+        MSTClusterBuilder clusterBuilder = new MSTClusterBuilder( list, event );
+        clusterBuilder.registerMetrics(metrics);
+        clusterBuilder.setThreshold(threshold);
+        clusterBuilder.setSeedDecision(seedDecision);
+        clusterBuilder.setPairDecision(pairDecision);
+        clusterBuilder.setOutputDecision(outputDecision);
+        List<Cluster> outputList = clusterBuilder.doClustering();
+        event.put(clusterName, outputList );
+    }
+
+  /**
+    * Set the threshold. This will apply to all subsequent events.
+    * Default value is 30 (i.e. 30mm with the GeometricalDistance metric).
+    * For more information on what the threshold does, see MSTClusterBuilder.
+    */
+    public void setThreshold(double value){
+        threshold = value;
+        System.out.println("Threshold: "+threshold);
+    }
+
+  /**
+    * Set the name of the output list of clusters which is written to the event
+    * at the end of each process() call.
+    * Default value is "MSTCluster " plus the input string given to the Constructor.
+    */
+    public void setClusterName(String string){
+        clusterName = string;
+        System.out.println("ClusterName: "+clusterName);
+    }
+
+  /**
+    * Specify the metric to be used by the MSTClusterBuilder.
+    * Default is GeometricalDistance.
+    */
+    public void registerMetrics(Metrics inMetrics){
+        metrics = inMetrics;
+    }
+
+  /**
+    * Specify the input DecisionMaker, which is applied to each hit/cluster
+    * before it is considered for clustering.
+    * Default is to accept hit or non-empty cluster.
+    */
+    public void setInputDecision(DecisionMakerSingle<Cluster> decision){
+        inputDecision = decision;}
+
+  /**
+    * Specify the seed DecisionMaker, which must be satisfied by the first
+    * hit in a cluster.
+    * Default is to accept hit or non-empty cluster.
+    */
+    public void setSeedDecision(DecisionMakerSingle<Cluster> decision){
+        seedDecision = decision;}
+
+  /**
+    * Specify the pair DecisionMaker, which is applied to pairs of hits
+    * being considered for clustering. This requirement is in addition to
+    * (logical AND) the requirement that the distance metric be below the threshold.
+    * Default is to accept hit or non-empty cluster.
+    */
+    public void setPairDecision(DecisionMakerPair<Cluster,Cluster> decision){
+        pairDecision = decision;}
+
+  /**
+    * Specify the output DecisionMaker, which is applied to each cluster
+    * in the output list. 
+    * Default is to accept hit or non-empty cluster.
+    */
+    public void setOutputDecision(DecisionMakerSingle<Cluster> decision){
+        outputDecision = decision;}
+
+  /**
+    * Add a list of hits/clusters to be clustered.
+    *
+    * @param tag Name of the list as stored in the EventHeader
+    */
+    public void addUserInputList(String tag){
+        listToGet.add(tag);
+    }
+
+  /**
+    * Wipes ALL information on lists of hits/clusters to use.
+    */
+    public void resetUserInputList(){listToGet.clear();}
+}

lcsim/src/org/lcsim/recon/cluster/mst
Metrics.java added at 1.1
diff -N Metrics.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Metrics.java	2 Jan 2006 21:17:03 -0000	1.1
@@ -0,0 +1,20 @@
+package org.lcsim.recon.cluster.mst; 
+
+import java.io.*; 
+import java.util.*; 
+
+import org.lcsim.event.Cluster;
+
+/**
+  * Interface describes a general distance metric for use in the MST algorithm.
+  */
+
+public interface Metrics {
+
+  /**
+    * Return the value of the metric for the given clusters.
+    * The value should be zero or positive.
+    */
+    public double getDistance(Cluster c1, Cluster c2);
+
+}

lcsim/src/org/lcsim/recon/cluster/mst
MinimumHitToHitDistance.java added at 1.1
diff -N MinimumHitToHitDistance.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MinimumHitToHitDistance.java	2 Jan 2006 21:17:03 -0000	1.1
@@ -0,0 +1,46 @@
+package org.lcsim.recon.cluster.mst; 
+
+import java.util.List; 
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import hep.physics.vec.BasicHep3Vector;
+import hep.physics.vec.Hep3Vector;
+import hep.physics.vec.VecOp;
+
+import org.lcsim.event.Cluster;
+
+/**
+  * A distance metric. This is the shortest distance between any hit in the
+  * first cluster and any hit in the second cluster.
+  */
+
+public class MinimumHitToHitDistance implements Metrics {
+
+  /**
+    * Simple constructor.
+    */
+    public MinimumHitToHitDistance(){}
+
+  /**
+    * Return the shortest distance between a hit in cluster1 and a hit in cluster2.
+    */
+    public double getDistance (Cluster cluster1, Cluster cluster2){
+      // get min hit-hit distance
+      double minDist = Double.NaN;
+      boolean firstCheck = true;
+      for (CalorimeterHit hit1 : cluster1.getCalorimeterHits()) {
+        for (CalorimeterHit hit2 : cluster2.getCalorimeterHits()) {
+          Hep3Vector vect1 = new BasicHep3Vector(hit1.getPosition());
+          Hep3Vector vect2 = new BasicHep3Vector(hit2.getPosition());
+          Hep3Vector displacement = VecOp.sub(vect1, vect2);
+          double dist = displacement.magnitude();
+          if (firstCheck || dist<minDist) {
+            minDist = dist;
+            firstCheck = false;
+          }
+        }
+      }
+      return minDist;
+    }
+
+}
CVSspam 0.2.8