5 added files
lcsim/src/org/lcsim/recon/cluster/mst
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
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
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
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
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